From 32c5643139c6fbca560946d11d1d200fa1e41abb Mon Sep 17 00:00:00 2001 From: JuanGalilea Date: Mon, 11 May 2026 13:21:27 +0200 Subject: [PATCH 1/5] base issues action --- .github/workflows/claude.yaml | 135 ++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 .github/workflows/claude.yaml diff --git a/.github/workflows/claude.yaml b/.github/workflows/claude.yaml new file mode 100644 index 0000000..772fb95 --- /dev/null +++ b/.github/workflows/claude.yaml @@ -0,0 +1,135 @@ +name: Claude Code + +on: + workflow_call: + inputs: + ZENHUB_WORKSPACE_ID: + type: string + required: true + description: Zenhub workspace ID + secrets: + ANTHROPIC_API_KEY: + required: true + description: Claude API key + ZENHUB_TOKEN: + required: true + description: Zenhub token + GITHUB_TOKEN: + required: true + description: GitHub token + +jobs: + claude: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + actions: read + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'claude' && !contains(github.event.issue.labels.*.name, 'claude:done')) || + (github.event_name == 'issues' && github.event.action != 'labeled' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + track_progress: true + + - name: Move issue to ZenHub QA pipeline + if: | + github.event_name == 'issues' && + github.event.action == 'labeled' && + github.event.label.name == 'claude' && + steps.claude.conclusion == 'success' + env: + ZENHUB_TOKEN: ${{ secrets.ZENHUB_TOKEN }} + ZENHUB_WORKSPACE_ID: ${{ inputs.ZENHUB_WORKSPACE_ID }} + REPO_GH_ID: ${{ github.event.repository.id }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + run: | + set +e + GQL="https://api.zenhub.com/public/graphql" + + zenhub_query() { + curl -s -X POST "$GQL" \ + -H "Authorization: Bearer $ZENHUB_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$1" + } + + # Step 1: Get pipelines for workspace (using ZENHUB_WORKSPACE_ID directly) + WORKSPACE_ID="$ZENHUB_WORKSPACE_ID" + if [ -z "$WORKSPACE_ID" ]; then + echo "ERROR: ZENHUB_WORKSPACE_ID secret is not set." + exit 1 + fi + PIPELINE_DATA=$(zenhub_query "{\"query\": \"query(\$wsId: ID!) { workspace(id: \$wsId) { pipelinesConnection(first: 50) { nodes { id name } } } }\", \"variables\": {\"wsId\": \"$WORKSPACE_ID\"}}") + + PIPELINE_ID=$(echo "$PIPELINE_DATA" | jq -r '.data.workspace.pipelinesConnection.nodes[] | select(.name == "Review/QA") | .id' 2>/dev/null) + + if [ -z "$PIPELINE_ID" ]; then + echo "Available pipelines:" + echo "$PIPELINE_DATA" | jq -r '.data.workspace.pipelinesConnection.nodes[]? | " \(.name) → \(.id)"' 2>/dev/null + echo "ERROR: Pipeline 'Review/QA' not found or workspace query failed." + exit 1 + fi + # Step 2: Get ZenHub issue ID + ISSUE_DATA=$(zenhub_query "{\"query\": \"query(\$repoGhId: Int!, \$issueNumber: Int!) { issueByInfo(repositoryGhId: \$repoGhId, issueNumber: \$issueNumber) { id } }\", \"variables\": {\"repoGhId\": $REPO_GH_ID, \"issueNumber\": $ISSUE_NUMBER}}") + + ISSUE_ID=$(echo "$ISSUE_DATA" | jq -r '.data.issueByInfo.id' 2>/dev/null) + if [ -z "$ISSUE_ID" ] || [ "$ISSUE_ID" = "null" ]; then + echo "ERROR: Could not get ZenHub issue ID." + exit 1 + fi + # Step 3: Move issue to Review/QA + MOVE_RESULT=$(zenhub_query "{\"query\": \"mutation(\$input: MoveIssueInput!) { moveIssue(input: \$input) { issue { id } } }\", \"variables\": {\"input\": {\"issueId\": \"$ISSUE_ID\", \"pipelineId\": \"$PIPELINE_ID\", \"position\": 0}}}") + + if echo "$MOVE_RESULT" | jq -e '.errors' >/dev/null 2>&1; then + echo "ERROR: Move mutation failed." + exit 1 + fi + + echo "Issue #$ISSUE_NUMBER moved to Review/QA." + + - name: Mark issue as processed by Claude + if: | + github.event_name == 'issues' && + github.event.action == 'labeled' && + github.event.label.name == 'claude' && + steps.claude.conclusion == 'success' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Create label if it doesn't exist yet + try { + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'claude:done', + color: '7057ff', + description: 'Already processed by Claude' + }); + } catch (e) { + // Label already exists, ignore + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['claude:done'] + }); + + console.log('Label claude:done added to issue.'); \ No newline at end of file From b07f940cb19a0923987f353f4ee100f4f53338f9 Mon Sep 17 00:00:00 2001 From: JuanGalilea Date: Mon, 11 May 2026 13:32:31 +0200 Subject: [PATCH 2/5] trigger locally --- .github/workflows/claude.yaml | 47 +++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/.github/workflows/claude.yaml b/.github/workflows/claude.yaml index 772fb95..b7ae34c 100644 --- a/.github/workflows/claude.yaml +++ b/.github/workflows/claude.yaml @@ -1,22 +1,30 @@ name: Claude Code on: - workflow_call: - inputs: - ZENHUB_WORKSPACE_ID: - type: string - required: true - description: Zenhub workspace ID - secrets: - ANTHROPIC_API_KEY: - required: true - description: Claude API key - ZENHUB_TOKEN: - required: true - description: Zenhub token - GITHUB_TOKEN: - required: true - description: GitHub token + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned, labeled] + pull_request_review: + types: [submitted] + # workflow_call: + # inputs: + # ZENHUB_WORKSPACE_ID: + # type: string + # required: true + # description: Zenhub workspace ID + # secrets: + # ANTHROPIC_API_KEY: + # required: true + # description: Claude API key + # ZENHUB_TOKEN: + # required: true + # description: Zenhub token + # GITHUB_TOKEN: + # required: true + # description: GitHub token jobs: claude: @@ -51,10 +59,11 @@ jobs: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'claude' && - steps.claude.conclusion == 'success' + steps.claude.conclusion == 'success' && + 'meme' == 'lala' env: - ZENHUB_TOKEN: ${{ secrets.ZENHUB_TOKEN }} - ZENHUB_WORKSPACE_ID: ${{ inputs.ZENHUB_WORKSPACE_ID }} + # ZENHUB_TOKEN: ${{ secrets.ZENHUB_TOKEN }} + # ZENHUB_WORKSPACE_ID: ${{ inputs.ZENHUB_WORKSPACE_ID }} REPO_GH_ID: ${{ github.event.repository.id }} ISSUE_NUMBER: ${{ github.event.issue.number }} run: | From 39b65dc3fd618cca8c0e0da54c590c26dd2923d4 Mon Sep 17 00:00:00 2001 From: JuanGalilea Date: Mon, 11 May 2026 16:30:34 +0200 Subject: [PATCH 3/5] model switch and get rid of zenhub since claude is not creating PRs --- .github/workflows/claude.yaml | 92 +++++------------------------------ 1 file changed, 11 insertions(+), 81 deletions(-) diff --git a/.github/workflows/claude.yaml b/.github/workflows/claude.yaml index b7ae34c..abe7402 100644 --- a/.github/workflows/claude.yaml +++ b/.github/workflows/claude.yaml @@ -1,30 +1,14 @@ name: Claude Code on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned, labeled] - pull_request_review: - types: [submitted] - # workflow_call: - # inputs: - # ZENHUB_WORKSPACE_ID: - # type: string - # required: true - # description: Zenhub workspace ID - # secrets: - # ANTHROPIC_API_KEY: - # required: true - # description: Claude API key - # ZENHUB_TOKEN: - # required: true - # description: Zenhub token - # GITHUB_TOKEN: - # required: true - # description: GitHub token + workflow_call: + secrets: + ANTHROPIC_API_KEY: + required: true + description: Claude API key + GITHUB_TOKEN: + required: true + description: GitHub token jobs: claude: @@ -53,63 +37,9 @@ jobs: with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} track_progress: true - - - name: Move issue to ZenHub QA pipeline - if: | - github.event_name == 'issues' && - github.event.action == 'labeled' && - github.event.label.name == 'claude' && - steps.claude.conclusion == 'success' && - 'meme' == 'lala' - env: - # ZENHUB_TOKEN: ${{ secrets.ZENHUB_TOKEN }} - # ZENHUB_WORKSPACE_ID: ${{ inputs.ZENHUB_WORKSPACE_ID }} - REPO_GH_ID: ${{ github.event.repository.id }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - run: | - set +e - GQL="https://api.zenhub.com/public/graphql" - - zenhub_query() { - curl -s -X POST "$GQL" \ - -H "Authorization: Bearer $ZENHUB_TOKEN" \ - -H "Content-Type: application/json" \ - -d "$1" - } - - # Step 1: Get pipelines for workspace (using ZENHUB_WORKSPACE_ID directly) - WORKSPACE_ID="$ZENHUB_WORKSPACE_ID" - if [ -z "$WORKSPACE_ID" ]; then - echo "ERROR: ZENHUB_WORKSPACE_ID secret is not set." - exit 1 - fi - PIPELINE_DATA=$(zenhub_query "{\"query\": \"query(\$wsId: ID!) { workspace(id: \$wsId) { pipelinesConnection(first: 50) { nodes { id name } } } }\", \"variables\": {\"wsId\": \"$WORKSPACE_ID\"}}") - - PIPELINE_ID=$(echo "$PIPELINE_DATA" | jq -r '.data.workspace.pipelinesConnection.nodes[] | select(.name == "Review/QA") | .id' 2>/dev/null) - - if [ -z "$PIPELINE_ID" ]; then - echo "Available pipelines:" - echo "$PIPELINE_DATA" | jq -r '.data.workspace.pipelinesConnection.nodes[]? | " \(.name) → \(.id)"' 2>/dev/null - echo "ERROR: Pipeline 'Review/QA' not found or workspace query failed." - exit 1 - fi - # Step 2: Get ZenHub issue ID - ISSUE_DATA=$(zenhub_query "{\"query\": \"query(\$repoGhId: Int!, \$issueNumber: Int!) { issueByInfo(repositoryGhId: \$repoGhId, issueNumber: \$issueNumber) { id } }\", \"variables\": {\"repoGhId\": $REPO_GH_ID, \"issueNumber\": $ISSUE_NUMBER}}") - - ISSUE_ID=$(echo "$ISSUE_DATA" | jq -r '.data.issueByInfo.id' 2>/dev/null) - if [ -z "$ISSUE_ID" ] || [ "$ISSUE_ID" = "null" ]; then - echo "ERROR: Could not get ZenHub issue ID." - exit 1 - fi - # Step 3: Move issue to Review/QA - MOVE_RESULT=$(zenhub_query "{\"query\": \"mutation(\$input: MoveIssueInput!) { moveIssue(input: \$input) { issue { id } } }\", \"variables\": {\"input\": {\"issueId\": \"$ISSUE_ID\", \"pipelineId\": \"$PIPELINE_ID\", \"position\": 0}}}") - - if echo "$MOVE_RESULT" | jq -e '.errors' >/dev/null 2>&1; then - echo "ERROR: Move mutation failed." - exit 1 - fi - - echo "Issue #$ISSUE_NUMBER moved to Review/QA." + use_commit_signing: true + claude_args: | + --model claude-sonnet-4-6 - name: Mark issue as processed by Claude if: | From 9ec1ad30aacdb51155a5d59116e48d2b9ae0d751 Mon Sep 17 00:00:00 2001 From: JuanGalilea Date: Tue, 12 May 2026 16:10:45 +0200 Subject: [PATCH 4/5] Pr creation --- .github/workflows/claude.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/claude.yaml b/.github/workflows/claude.yaml index abe7402..e46ff8c 100644 --- a/.github/workflows/claude.yaml +++ b/.github/workflows/claude.yaml @@ -40,6 +40,7 @@ jobs: use_commit_signing: true claude_args: | --model claude-sonnet-4-6 + --allowedTools "Bash(gh pr create*),Bash(gh api*)" - name: Mark issue as processed by Claude if: | From d5ec2a4070be2ea4780ff0c96c18267ff95bd47f Mon Sep 17 00:00:00 2001 From: JuanGalilea Date: Tue, 12 May 2026 16:20:00 +0200 Subject: [PATCH 5/5] vlada's comment --- .github/workflows/claude.yaml | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/.github/workflows/claude.yaml b/.github/workflows/claude.yaml index e46ff8c..f505829 100644 --- a/.github/workflows/claude.yaml +++ b/.github/workflows/claude.yaml @@ -52,24 +52,14 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - // Create label if it doesn't exist yet try { - await github.rest.issues.createLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - name: 'claude:done', - color: '7057ff', - description: 'Already processed by Claude' - }); + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: ['claude:done'] + }); + console.log('Label claude:done added to issue.'); } catch (e) { - // Label already exists, ignore + console.log('Label claude:done does not exist. Skipping.'); } - - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - labels: ['claude:done'] - }); - - console.log('Label claude:done added to issue.'); \ No newline at end of file