From f3e800e254a2700adc5bc3a13745388e4e64aaa9 Mon Sep 17 00:00:00 2001 From: Vlad Mocanu Date: Fri, 27 Feb 2026 13:21:52 +0200 Subject: [PATCH] fix: use integer label IDs for Forgejo issue creation Forgejo's create-issue API expects `labels` as `[int64]` (label IDs), not `[{"name": "..."}]` like GitHub. The previous format caused a JSON unmarshal error, silently returning null instead of creating the issue. Changes: - Split label step to create-if-missing then look up the ID - Pass label ID as integer to issue creation: `labels: [169]` - Add error handling: log API response if issue creation returns null - Also cleaned up another duplicate upstream-sync label Co-Authored-By: Claude Opus 4.6 --- .forgejo/kcl-ci/main.k | 45 ++++++++++++++++++--------- .forgejo/workflows/upstream-sync.yml | 46 ++++++++++++++++++---------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/.forgejo/kcl-ci/main.k b/.forgejo/kcl-ci/main.k index 7851767..e7cef80 100644 --- a/.forgejo/kcl-ci/main.k +++ b/.forgejo/kcl-ci/main.k @@ -389,21 +389,34 @@ else fi""" } { - name = "Create upstream-sync label" + name = "Ensure upstream-sync label exists" "if" = h.gha_steps_eq("compare", "needs_sync", "true") + id = "label" run = """\ +API="${h.gha_env("GITHUB_API_URL")}/repos/${h.gha_github("repository")}" +AUTH="Authorization: token ${h.gha_secret("CLAUDE_BOT_TOKEN")}" + +# Try to create the label (409 = already exists, which is fine) curl -s -o /dev/null -w "%{http_code}" \\ - -X POST \\ - -H "Authorization: token ${h.gha_secret("CLAUDE_BOT_TOKEN")}" \\ - -H "Content-Type: application/json" \\ + -X POST -H "${"$"}AUTH" -H "Content-Type: application/json" \\ -d '{"name":"upstream-sync","color":"#0075ca","description":"Upstream sync tracking"}' \\ - "${h.gha_env("GITHUB_API_URL")}/repos/${h.gha_github("repository")}/labels" || true""" + "${"$"}API/labels" || true + +# Look up the label ID (Forgejo needs integer IDs, not names) +LABEL_ID=${"$"}(curl -s -H "${"$"}AUTH" "${"$"}API/labels" | jq -r '[.[] | select(.name == "upstream-sync")] | .[0].id // ""') +echo "label_id=${"$"}LABEL_ID" >> "${"$"}GITHUB_OUTPUT" +echo "upstream-sync label ID: ${"$"}LABEL_ID" +""" } { name = "Create or update sync issue" "if" = h.gha_steps_eq("compare", "needs_sync", "true") id = "sync-issue" run = """\ +API="${h.gha_env("GITHUB_API_URL")}/repos/${h.gha_github("repository")}" +AUTH="Authorization: token ${h.gha_secret("CLAUDE_BOT_TOKEN")}" +LABEL_ID="${h.gha_steps("label", "label_id")}" + BEHIND="${h.gha_steps("compare", "behind")}" AHEAD="${h.gha_steps("compare", "ahead")}" COMMIT_LOG=${"$"}(cat <<'LOG_EOF' @@ -460,22 +473,24 @@ TITLE="Upstream sync: ${"$"}{BEHIND} new commits from anthropics/claude-code-act if [ "${h.gha_steps("existing", "issue_exists")}" = "true" ]; then ISSUE_NUMBER="${h.gha_steps("existing", "issue_number")}" curl -s -o /dev/null \\ - -X PATCH \\ - -H "Authorization: token ${h.gha_secret("CLAUDE_BOT_TOKEN")}" \\ - -H "Content-Type: application/json" \\ + -X PATCH -H "${"$"}AUTH" -H "Content-Type: application/json" \\ -d "${"$"}(jq -n --arg title "${"$"}TITLE" --arg body "${"$"}BODY" '{title: ${"$"}title, body: ${"$"}body}')" \\ - "${h.gha_env("GITHUB_API_URL")}/repos/${h.gha_github("repository")}/issues/${"$"}{ISSUE_NUMBER}" + "${"$"}API/issues/${"$"}{ISSUE_NUMBER}" echo "Updated existing issue #${"$"}{ISSUE_NUMBER}" echo "issue_number=${"$"}{ISSUE_NUMBER}" >> "${"$"}GITHUB_OUTPUT" else + # Forgejo expects labels as integer IDs, not objects with names RESPONSE=${"$"}(curl -s \\ - -X POST \\ - -H "Authorization: token ${h.gha_secret("CLAUDE_BOT_TOKEN")}" \\ - -H "Content-Type: application/json" \\ - -d "${"$"}(jq -n --arg title "${"$"}TITLE" --arg body "${"$"}BODY" '{title: ${"$"}title, body: ${"$"}body, labels: [{"name": "upstream-sync"}]}')" \\ - "${h.gha_env("GITHUB_API_URL")}/repos/${h.gha_github("repository")}/issues") + -X POST -H "${"$"}AUTH" -H "Content-Type: application/json" \\ + -d "${"$"}(jq -n --arg title "${"$"}TITLE" --arg body "${"$"}BODY" --argjson labels "[${"$"}{LABEL_ID:-0}]" '{title: ${"$"}title, body: ${"$"}body, labels: ${"$"}labels}')" \\ + "${"$"}API/issues") ISSUE_NUMBER=${"$"}(echo "${"$"}RESPONSE" | jq '.number') - echo "Created new upstream sync issue #${"$"}{ISSUE_NUMBER}" + if [ "${"$"}ISSUE_NUMBER" = "null" ] || [ -z "${"$"}ISSUE_NUMBER" ]; then + echo "::error::Failed to create issue. API response:" + echo "${"$"}RESPONSE" | jq '.' + else + echo "Created new upstream sync issue #${"$"}{ISSUE_NUMBER}" + fi echo "issue_number=${"$"}{ISSUE_NUMBER}" >> "${"$"}GITHUB_OUTPUT" fi""" } diff --git a/.forgejo/workflows/upstream-sync.yml b/.forgejo/workflows/upstream-sync.yml index 3705f06..628dcf9 100644 --- a/.forgejo/workflows/upstream-sync.yml +++ b/.forgejo/workflows/upstream-sync.yml @@ -88,19 +88,31 @@ jobs: echo "issue_exists=false" >> "$GITHUB_OUTPUT" echo "No existing sync issue found" fi - - name: Create upstream-sync label + - name: Ensure upstream-sync label exists if: steps.compare.outputs.needs_sync == 'true' - run: |- + id: label + run: | + API="${{ env.GITHUB_API_URL }}/repos/${{ github.repository }}" + AUTH="Authorization: token ${{ secrets.CLAUDE_BOT_TOKEN }}" + + # Try to create the label (409 = already exists, which is fine) curl -s -o /dev/null -w "%{http_code}" \ - -X POST \ - -H "Authorization: token ${{ secrets.CLAUDE_BOT_TOKEN }}" \ - -H "Content-Type: application/json" \ + -X POST -H "$AUTH" -H "Content-Type: application/json" \ -d '{"name":"upstream-sync","color":"#0075ca","description":"Upstream sync tracking"}' \ - "${{ env.GITHUB_API_URL }}/repos/${{ github.repository }}/labels" || true + "$API/labels" || true + + # Look up the label ID (Forgejo needs integer IDs, not names) + LABEL_ID=$(curl -s -H "$AUTH" "$API/labels" | jq -r '[.[] | select(.name == "upstream-sync")] | .[0].id // ""') + echo "label_id=$LABEL_ID" >> "$GITHUB_OUTPUT" + echo "upstream-sync label ID: $LABEL_ID" - name: Create or update sync issue if: steps.compare.outputs.needs_sync == 'true' id: sync-issue run: |- + API="${{ env.GITHUB_API_URL }}/repos/${{ github.repository }}" + AUTH="Authorization: token ${{ secrets.CLAUDE_BOT_TOKEN }}" + LABEL_ID="${{ steps.label.outputs.label_id }}" + BEHIND="${{ steps.compare.outputs.behind }}" AHEAD="${{ steps.compare.outputs.ahead }}" COMMIT_LOG=$(cat <<'LOG_EOF' @@ -157,22 +169,24 @@ jobs: if [ "${{ steps.existing.outputs.issue_exists }}" = "true" ]; then ISSUE_NUMBER="${{ steps.existing.outputs.issue_number }}" curl -s -o /dev/null \ - -X PATCH \ - -H "Authorization: token ${{ secrets.CLAUDE_BOT_TOKEN }}" \ - -H "Content-Type: application/json" \ + -X PATCH -H "$AUTH" -H "Content-Type: application/json" \ -d "$(jq -n --arg title "$TITLE" --arg body "$BODY" '{title: $title, body: $body}')" \ - "${{ env.GITHUB_API_URL }}/repos/${{ github.repository }}/issues/${ISSUE_NUMBER}" + "$API/issues/${ISSUE_NUMBER}" echo "Updated existing issue #${ISSUE_NUMBER}" echo "issue_number=${ISSUE_NUMBER}" >> "$GITHUB_OUTPUT" else + # Forgejo expects labels as integer IDs, not objects with names RESPONSE=$(curl -s \ - -X POST \ - -H "Authorization: token ${{ secrets.CLAUDE_BOT_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg title "$TITLE" --arg body "$BODY" '{title: $title, body: $body, labels: [{"name": "upstream-sync"}]}')" \ - "${{ env.GITHUB_API_URL }}/repos/${{ github.repository }}/issues") + -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d "$(jq -n --arg title "$TITLE" --arg body "$BODY" --argjson labels "[${LABEL_ID:-0}]" '{title: $title, body: $body, labels: $labels}')" \ + "$API/issues") ISSUE_NUMBER=$(echo "$RESPONSE" | jq '.number') - echo "Created new upstream sync issue #${ISSUE_NUMBER}" + if [ "$ISSUE_NUMBER" = "null" ] || [ -z "$ISSUE_NUMBER" ]; then + echo "::error::Failed to create issue. API response:" + echo "$RESPONSE" | jq '.' + else + echo "Created new upstream sync issue #${ISSUE_NUMBER}" + fi echo "issue_number=${ISSUE_NUMBER}" >> "$GITHUB_OUTPUT" fi merge-upstream: