# 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