From 4b4fafc79fa38ae1aa883887a27651d81d4bad9d Mon Sep 17 00:00:00 2001 From: Rodrigo Toshiaki Horie Date: Wed, 17 Jun 2026 15:44:05 -0300 Subject: [PATCH] fix: use GitHub API for signed commits in spec sync workflow (#16509) The aap-openapi-specs repo requires commit signatures via org ruleset. Switch from git commit+push to the GitHub Git Data API which automatically signs commits, satisfying the required_signatures rule. Co-authored-by: Claude Opus 4.6 (1M context) --- .github/workflows/spec-sync-on-merge.yml | 89 ++++++++++++++++-------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/.github/workflows/spec-sync-on-merge.yml b/.github/workflows/spec-sync-on-merge.yml index c21d03e6bc..fd62c4034f 100644 --- a/.github/workflows/spec-sync-on-merge.yml +++ b/.github/workflows/spec-sync-on-merge.yml @@ -43,9 +43,12 @@ jobs: private-github-key: ${{ secrets.PRIVATE_GITHUB_KEY }} - name: Generate API Schema + env: + REF_NAME: ${{ github.ref_name }} + BASE_REF: ${{ github.base_ref }} run: | DEV_DOCKER_TAG_BASE=ghcr.io/${OWNER_LC} \ - COMPOSE_TAG=${{ github.base_ref || github.ref_name }} \ + COMPOSE_TAG=${BASE_REF:-${REF_NAME}} \ docker run -u $(id -u) --rm -v ${{ github.workspace }}:/awx_devel/:Z \ --workdir=/awx_devel `make print-DEVEL_IMAGE_NAME` /start_tests.sh genschema @@ -72,9 +75,11 @@ jobs: - name: Fail if branch doesn't exist if: steps.checkout_spec_repo.outcome == 'failure' + env: + REF_NAME: ${{ github.ref_name }} run: | - echo "##[error]❌ Branch '${{ github.ref_name }}' does not exist in the central spec repository." - echo "##[error]Expected branch: ${{ github.ref_name }}" + echo "##[error]❌ Branch '${REF_NAME}' does not exist in the central spec repository." + echo "##[error]Expected branch: ${REF_NAME}" echo "##[error]This branch must be created in the spec repo before specs can be synced." exit 1 @@ -113,48 +118,69 @@ jobs: env: GH_TOKEN: ${{ secrets.OPENAPI_SPEC_SYNC_TOKEN }} COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + SPEC_REPO: ansible-automation-platform/aap-openapi-specs + REF_NAME: ${{ github.ref_name }} + GITHUB_SHA_FULL: ${{ github.sha }} + GITHUB_REPO: ${{ github.repository }} + IS_NEW_FILE: ${{ steps.compare.outputs.is_new_file }} run: | - # Configure git - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + SHORT_SHA="${GITHUB_SHA_FULL:0:7}" + BRANCH_NAME="update-Controller-${REF_NAME}-${SHORT_SHA}" - # Create branch for PR - SHORT_SHA="${{ github.sha }}" - SHORT_SHA="${SHORT_SHA:0:7}" - BRANCH_NAME="update-Controller-${{ github.ref_name }}-${SHORT_SHA}" - git checkout -b "$BRANCH_NAME" - - # Add and commit changes - git add "controller.json" - - if [ "${{ steps.compare.outputs.is_new_file }}" == "true" ]; then - COMMIT_MSG="Add Controller OpenAPI spec for ${{ github.ref_name }}" + if [ "${IS_NEW_FILE}" == "true" ]; then + COMMIT_MSG="Add Controller OpenAPI spec for ${REF_NAME}" else - COMMIT_MSG="Update Controller OpenAPI spec for ${{ github.ref_name }}" + COMMIT_MSG="Update Controller OpenAPI spec for ${REF_NAME}" fi - git commit -m "$COMMIT_MSG + COMMIT_MSG="${COMMIT_MSG} - Synced from ${{ github.repository }}@${{ github.sha }} - Source branch: ${{ github.ref_name }} + Synced from ${GITHUB_REPO}@${GITHUB_SHA_FULL} + Source branch: ${REF_NAME}" - Co-Authored-By: github-actions[bot] " + # Create branch via API + BASE_SHA=$(gh api "repos/${SPEC_REPO}/git/ref/heads/${REF_NAME}" --jq '.object.sha') + gh api "repos/${SPEC_REPO}/git/refs" \ + -f "ref=refs/heads/${BRANCH_NAME}" \ + -f "sha=${BASE_SHA}" - # Push branch - git push origin "$BRANCH_NAME" + # Get the tree SHA from the base commit (base_tree requires a tree SHA, not a commit SHA) + BASE_TREE_SHA=$(gh api "repos/${SPEC_REPO}/git/commits/${BASE_SHA}" --jq '.tree.sha') + + # Create blob and commit via API (commits created through the API are automatically signed by GitHub) + BLOB_SHA=$(gh api "repos/${SPEC_REPO}/git/blobs" \ + -f "content=$(base64 -w 0 controller.json)" \ + -f "encoding=base64" \ + --jq '.sha') + + TREE_SHA=$(gh api "repos/${SPEC_REPO}/git/trees" \ + -f "base_tree=${BASE_TREE_SHA}" \ + --input <(jq -n --arg blob "$BLOB_SHA" '{tree: [{path: "controller.json", mode: "100644", type: "blob", sha: $blob}]}') \ + --jq '.sha') + + NEW_COMMIT_SHA=$(gh api "repos/${SPEC_REPO}/git/commits" \ + -f "message=${COMMIT_MSG}" \ + -f "tree=${TREE_SHA}" \ + -f "parents[]=${BASE_SHA}" \ + --jq '.sha') + + # Update branch ref to point to the new signed commit + gh api "repos/${SPEC_REPO}/git/refs/heads/${BRANCH_NAME}" \ + -X PATCH \ + -f "sha=${NEW_COMMIT_SHA}" # Create PR - PR_TITLE="[${{ github.ref_name }}] Update Controller spec from merged commit" + PR_TITLE="[${REF_NAME}] Update Controller spec from merged commit" PR_BODY="## Summary Automated OpenAPI spec sync from component repository merge. - **Source:** ${{ github.repository }}@${{ github.sha }} - **Branch:** \`${{ github.ref_name }}\` + **Source:** ${GITHUB_REPO}@${GITHUB_SHA_FULL} + **Branch:** \`${REF_NAME}\` **Component:** \`Controller\` **Spec File:** \`controller.json\` ## Changes - $(if [ "${{ steps.compare.outputs.is_new_file }}" == "true" ]; then echo "- 🆕 New spec file created"; else echo "- 📝 Spec file updated with latest changes"; fi) + $(if [ "${IS_NEW_FILE}" == "true" ]; then echo "- 🆕 New spec file created"; else echo "- 📝 Spec file updated with latest changes"; fi) ## Source Commit \`\`\` @@ -165,17 +191,20 @@ jobs: 🤖 This PR was automatically generated by the OpenAPI spec sync workflow." gh pr create \ + --repo "${SPEC_REPO}" \ --title "$PR_TITLE" \ --body "$PR_BODY" \ - --base "${{ github.ref_name }}" \ + --base "${REF_NAME}" \ --head "$BRANCH_NAME" echo "✅ Created PR in spec repo" - name: Report results if: always() + env: + HAS_DIFF: ${{ steps.compare.outputs.has_diff }} run: | - if [ "${{ steps.compare.outputs.has_diff }}" == "true" ]; then + if [ "${HAS_DIFF}" == "true" ]; then echo "📝 Spec sync completed - PR created in spec repo" else echo "✅ Spec sync completed - no changes needed"