Files
awx/.github/workflows/spec-sync-on-merge.yml
Rodrigo Toshiaki Horie 4b4fafc79f 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) <noreply@anthropic.com>
2026-06-17 18:44:05 +00:00

212 lines
7.8 KiB
YAML

# Sync OpenAPI Spec on Merge
#
# This workflow runs when code is merged to the devel branch.
# It runs the dev environment to generate the OpenAPI spec, then syncs it to
# the central spec repository.
#
# FLOW: PR merged → push to branch → dev environment runs → spec synced to central repo
#
# NOTE: This is an inlined version for testing with private forks.
# Production version will use a reusable workflow from the org repos.
name: Sync OpenAPI Spec on Merge
env:
LC_ALL: "C.UTF-8"
DEV_DOCKER_OWNER: ${{ github.repository_owner }}
on:
push:
branches:
- devel
- 'stable-2.[6-9]'
- 'stable-2.[1-9][0-9]'
workflow_dispatch: # Allow manual triggering for testing
jobs:
sync-openapi-spec:
if: |
github.event_name == 'workflow_dispatch' ||
(github.repository == 'ansible/awx' && (github.ref_name == 'devel' || startsWith(github.ref_name, 'feature_'))) ||
(github.repository == 'ansible/tower' && (startsWith(github.ref_name, 'stable-') || startsWith(github.ref_name, 'release_')))
name: Sync OpenAPI spec to central repo
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Checkout Controller repository
uses: actions/checkout@v4
with:
show-progress: false
- name: Build awx_devel image to use for schema gen
uses: ./.github/actions/awx_devel_image
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
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=${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
- name: Verify spec file exists
run: |
SPEC_FILE="./schema.json"
if [ ! -f "$SPEC_FILE" ]; then
echo "❌ Spec file not found at $SPEC_FILE"
echo "Contents of workspace:"
ls -la .
exit 1
fi
echo "✅ Found spec file at $SPEC_FILE"
- name: Checkout spec repo
id: checkout_spec_repo
continue-on-error: true
uses: actions/checkout@v4
with:
repository: ansible-automation-platform/aap-openapi-specs
ref: ${{ github.ref_name }}
path: spec-repo
token: ${{ secrets.OPENAPI_SPEC_SYNC_TOKEN }}
- name: Fail if branch doesn't exist
if: steps.checkout_spec_repo.outcome == 'failure'
env:
REF_NAME: ${{ github.ref_name }}
run: |
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
- name: Compare specs
id: compare
run: |
COMPONENT_SPEC="./schema.json"
SPEC_REPO_FILE="spec-repo/controller.json"
# Check if spec file exists in spec repo
if [ ! -f "$SPEC_REPO_FILE" ]; then
echo "Spec file doesn't exist in spec repo - will create new file"
echo "has_diff=true" >> $GITHUB_OUTPUT
echo "is_new_file=true" >> $GITHUB_OUTPUT
else
# Compare files
if diff -q "$COMPONENT_SPEC" "$SPEC_REPO_FILE" > /dev/null; then
echo "✅ No differences found - specs are identical"
echo "has_diff=false" >> $GITHUB_OUTPUT
else
echo "📝 Differences found - spec has changed"
echo "has_diff=true" >> $GITHUB_OUTPUT
echo "is_new_file=false" >> $GITHUB_OUTPUT
fi
fi
- name: Update spec file
if: steps.compare.outputs.has_diff == 'true'
run: |
cp "./schema.json" "spec-repo/controller.json"
echo "✅ Updated spec-repo/controller.json"
- name: Create PR in spec repo
if: steps.compare.outputs.has_diff == 'true'
working-directory: spec-repo
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: |
SHORT_SHA="${GITHUB_SHA_FULL:0:7}"
BRANCH_NAME="update-Controller-${REF_NAME}-${SHORT_SHA}"
if [ "${IS_NEW_FILE}" == "true" ]; then
COMMIT_MSG="Add Controller OpenAPI spec for ${REF_NAME}"
else
COMMIT_MSG="Update Controller OpenAPI spec for ${REF_NAME}"
fi
COMMIT_MSG="${COMMIT_MSG}
Synced from ${GITHUB_REPO}@${GITHUB_SHA_FULL}
Source branch: ${REF_NAME}"
# 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}"
# 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="[${REF_NAME}] Update Controller spec from merged commit"
PR_BODY="## Summary
Automated OpenAPI spec sync from component repository merge.
**Source:** ${GITHUB_REPO}@${GITHUB_SHA_FULL}
**Branch:** \`${REF_NAME}\`
**Component:** \`Controller\`
**Spec File:** \`controller.json\`
## Changes
$(if [ "${IS_NEW_FILE}" == "true" ]; then echo "- 🆕 New spec file created"; else echo "- 📝 Spec file updated with latest changes"; fi)
## Source Commit
\`\`\`
${COMMIT_MESSAGE}
\`\`\`
---
🤖 This PR was automatically generated by the OpenAPI spec sync workflow."
gh pr create \
--repo "${SPEC_REPO}" \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--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 [ "${HAS_DIFF}" == "true" ]; then
echo "📝 Spec sync completed - PR created in spec repo"
else
echo "✅ Spec sync completed - no changes needed"
fi