Update Claude workflow: add cost controls and model selection
This commit is contained in:
@@ -14,6 +14,7 @@ jobs:
|
|||||||
(github.event_name == 'issue_comment' &&
|
(github.event_name == 'issue_comment' &&
|
||||||
contains(github.event.comment.body, '@claude'))
|
contains(github.event.comment.body, '@claude'))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -23,21 +24,39 @@ jobs:
|
|||||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
# Configure git for Claude to use
|
set +e
|
||||||
|
|
||||||
|
# Configure git
|
||||||
git config user.name "Claude Bot"
|
git config user.name "Claude Bot"
|
||||||
git config user.email "claude@localhost"
|
git config user.email "claude@localhost"
|
||||||
git remote set-url origin "http://admin:${GIT_TOKEN}@localhost:3000/${{ github.repository }}.git"
|
git remote set-url origin "http://admin:${GIT_TOKEN}@localhost:3000/${{ github.repository }}.git"
|
||||||
|
|
||||||
# Extract issue info
|
|
||||||
ISSUE_NUMBER="${{ github.event.issue.number }}"
|
ISSUE_NUMBER="${{ github.event.issue.number }}"
|
||||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||||
REPO="${{ github.repository }}"
|
REPO="${{ github.repository }}"
|
||||||
|
LABELS_JSON='${{ toJSON(github.event.issue.labels) }}'
|
||||||
|
|
||||||
|
# Determine model + cost limits from issue labels
|
||||||
|
# Default: haiku (cheap). Add claude:sonnet or claude:opus for harder tasks.
|
||||||
|
CLAUDE_MODEL="haiku"
|
||||||
|
MAX_TURNS=15
|
||||||
|
MAX_BUDGET="0.50"
|
||||||
|
EFFORT="low"
|
||||||
|
if echo "$LABELS_JSON" | grep -q '"claude:opus"'; then
|
||||||
|
CLAUDE_MODEL="claude-opus-4-6"
|
||||||
|
MAX_TURNS=40
|
||||||
|
MAX_BUDGET="5.00"
|
||||||
|
EFFORT="high"
|
||||||
|
elif echo "$LABELS_JSON" | grep -q '"claude:sonnet"'; then
|
||||||
|
CLAUDE_MODEL="claude-sonnet-4-6"
|
||||||
|
MAX_TURNS=25
|
||||||
|
MAX_BUDGET="2.00"
|
||||||
|
EFFORT="medium"
|
||||||
|
fi
|
||||||
|
|
||||||
# Fetch issue body via API
|
|
||||||
ISSUE_BODY=$(curl -s "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}" \
|
ISSUE_BODY=$(curl -s "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}" \
|
||||||
-H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))")
|
-H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))")
|
||||||
|
|
||||||
# Get comment body if triggered by comment
|
|
||||||
COMMENT_BODY=""
|
COMMENT_BODY=""
|
||||||
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
if [ "${{ github.event_name }}" = "issue_comment" ]; then
|
||||||
COMMENT_ID="${{ github.event.comment.id }}"
|
COMMENT_ID="${{ github.event.comment.id }}"
|
||||||
@@ -45,17 +64,10 @@ jobs:
|
|||||||
-H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))")
|
-H "Authorization: token ${GIT_TOKEN}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('body',''))")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create working branch
|
|
||||||
BRANCH="claude/issue-${ISSUE_NUMBER}"
|
BRANCH="claude/issue-${ISSUE_NUMBER}"
|
||||||
git checkout -b "${BRANCH}"
|
git checkout -b "${BRANCH}"
|
||||||
|
|
||||||
# Post status comment
|
# Run Claude Code with cost controls
|
||||||
curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}/comments" \
|
|
||||||
-H "Authorization: token ${GIT_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"body\": \"Claude is working on this issue...\"}"
|
|
||||||
|
|
||||||
# Run Claude Code - it handles everything: code changes, commits, push, and PR creation
|
|
||||||
claude -p "You are working on the repository ${REPO} (Gitea instance at http://localhost:3000).
|
claude -p "You are working on the repository ${REPO} (Gitea instance at http://localhost:3000).
|
||||||
A Gitea issue needs your attention:
|
A Gitea issue needs your attention:
|
||||||
|
|
||||||
@@ -63,19 +75,55 @@ jobs:
|
|||||||
Description: ${ISSUE_BODY}
|
Description: ${ISSUE_BODY}
|
||||||
Additional context: ${COMMENT_BODY}
|
Additional context: ${COMMENT_BODY}
|
||||||
|
|
||||||
Instructions:
|
IMPORTANT RULES:
|
||||||
1. Read and understand the codebase
|
- Do NOT retry failed commands more than once. If something fails twice, stop and report the error.
|
||||||
|
- Do NOT loop on failing tests. Fix the obvious issue or report it. Never run the same failing command 3+ times.
|
||||||
|
- If you cannot complete the task, push what you have, create the PR as draft, and explain what is blocked.
|
||||||
|
- Be efficient: read only files you need, make targeted edits, avoid unnecessary exploration.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Read and understand the relevant parts of the codebase
|
||||||
2. Implement the requested changes
|
2. Implement the requested changes
|
||||||
3. Commit your changes with a descriptive message
|
3. Commit your changes with a descriptive message
|
||||||
4. Push branch ${BRANCH} to origin
|
4. Push branch ${BRANCH} to origin
|
||||||
5. Create a pull request targeting main that references issue #${ISSUE_NUMBER}
|
5. Create a pull request targeting main that references issue #${ISSUE_NUMBER}
|
||||||
6. Comment on issue #${ISSUE_NUMBER} with a link to the PR
|
6. Post a comment on issue #${ISSUE_NUMBER} summarizing what you did
|
||||||
|
|
||||||
Git is configured. You are on branch ${BRANCH}. Work in the current directory.
|
Git is configured. You are on branch ${BRANCH}. Work in the current directory.
|
||||||
Use git commands to push, and curl to the Gitea API for PR creation.
|
Use git commands to push, and curl to the Gitea API for PR creation and comments.
|
||||||
Gitea API token is available as env var GIT_TOKEN." \
|
Gitea API token is available as env var GIT_TOKEN." \
|
||||||
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
|
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
|
||||||
--mcp-config /home/claude-runner/.claude/mcp-gitea.json \
|
--model "${CLAUDE_MODEL}" \
|
||||||
--max-turns 50 \
|
--max-turns "${MAX_TURNS}" \
|
||||||
|
--max-budget-usd "${MAX_BUDGET}" \
|
||||||
|
--effort "${EFFORT}" \
|
||||||
--permission-mode bypassPermissions \
|
--permission-mode bypassPermissions \
|
||||||
--output-format text 2>&1 | tee /home/claude-runner/last-claude-output.txt
|
--output-format json 2>&1 > /tmp/claude-result.json
|
||||||
|
|
||||||
|
CLAUDE_EXIT=$?
|
||||||
|
|
||||||
|
# Extract cost from JSON output
|
||||||
|
COST=$(python3 -c "
|
||||||
|
import json
|
||||||
|
with open('/tmp/claude-result.json') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
cost = data.get('total_cost_usd', 0)
|
||||||
|
print(f'\${cost:.4f}')
|
||||||
|
" 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
# Amend the last commit to include cost and model
|
||||||
|
if git log --oneline main..HEAD 2>/dev/null | head -1 | grep -q .; then
|
||||||
|
LAST_MSG=$(git log -1 --format=%B)
|
||||||
|
git commit --amend -m "${LAST_MSG}
|
||||||
|
|
||||||
|
Claude model: ${CLAUDE_MODEL} | API cost: ${COST}" --no-verify
|
||||||
|
git push origin "${BRANCH}" --force
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Post cost as comment
|
||||||
|
curl -s -X POST "http://localhost:3000/api/v1/repos/${REPO}/issues/${ISSUE_NUMBER}/comments" \
|
||||||
|
-H "Authorization: token ${GIT_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"body\": \"Done (model: **${CLAUDE_MODEL}**, effort: ${EFFORT}, budget cap: \$${MAX_BUDGET}). API cost: **${COST}**\"}" > /dev/null
|
||||||
|
|
||||||
|
exit ${CLAUDE_EXIT}
|
||||||
|
|||||||
Reference in New Issue
Block a user