sync-api #1887
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This workflow will sync the API types from the cloudnative-pg repository to the API repository, and | |
| # tidy the go.mod file. | |
| # It is supposed to be triggered by a repository_dispatch event whenever the cloudnative-pg api is updated, | |
| # but can also be triggered manually using the "Sync API" workflow_dispatch event. | |
| name: Sync API | |
| on: | |
| workflow_dispatch: | |
| repository_dispatch: | |
| types: [ sync-api ] | |
| permissions: read-all | |
| # A release dispatches a tag-carrying sync while the push to main triggers a | |
| # second, tag-less sync. Serialize them so they don't race on the commit to main | |
| # and the tag-carrying run tags the already-synced commit. | |
| concurrency: | |
| group: sync-api | |
| cancel-in-progress: false | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout API repository | |
| uses: actions/checkout@v7 | |
| with: | |
| token: ${{ secrets.REPO_PAT }} | |
| - name: Checkout cloudnative-pg repository | |
| uses: actions/checkout@v7 | |
| with: | |
| path: cloudnative-pg | |
| repository: cloudnative-pg/cloudnative-pg | |
| sparse-checkout: api/v1 | |
| # Full history is required to replay each upstream commit individually. | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: cloudnative-pg/go.mod | |
| # Replay every upstream commit that touched the synced files as an individual | |
| # commit, reusing the original message and appending a | |
| # "(cherry picked from commit <sha>)" trailer (like `git cherry-pick -x`). | |
| # A literal cherry-pick is impossible because this repository rewrites the | |
| # module path and relocates the files to pkg/api/v1, so we reproduce each | |
| # upstream snapshot instead. The trailer doubles as the sync state anchor. | |
| - name: Sync API types from cloudnative-pg | |
| env: | |
| UPSTREAM: cloudnative-pg/cloudnative-pg | |
| run: | | |
| set -euo pipefail | |
| git config user.name "CloudNativePG Automated Updates" | |
| git config user.email "noreply@cnpg.com" | |
| # Find the last synced upstream commit recorded in our history. Our own | |
| # trailer is always the final line of the body, so take the last match | |
| # (upstream backport commits may carry their own cherry-pick line too). | |
| # The trailing hex run is the SHA regardless of the "<owner>/<repo>@" | |
| # prefix we add for cross-repo linking. | |
| LAST=$(git log --grep='cherry picked from commit' -n1 --pretty=%B \ | |
| | grep -iE 'cherry picked from commit' \ | |
| | tail -n1 \ | |
| | grep -oiE '[0-9a-f]{7,40}' \ | |
| | tail -n1) || true | |
| # Build the list of upstream commits to replay, oldest first. | |
| if [ -n "${LAST:-}" ]; then | |
| mapfile -t COMMITS < <(git -C cloudnative-pg log --reverse --pretty=%H "$LAST..HEAD" -- api/v1 go.mod go.sum) | |
| else | |
| # Bootstrap: no anchor yet, seed from the current upstream HEAD only. | |
| mapfile -t COMMITS < <(git -C cloudnative-pg rev-parse HEAD) | |
| fi | |
| if [ ${#COMMITS[@]} -eq 0 ]; then | |
| echo "Already up to date with upstream; nothing to sync." | |
| exit 0 | |
| fi | |
| for sha in "${COMMITS[@]}"; do | |
| git -C cloudnative-pg checkout -q "$sha" -- api/v1 go.mod go.sum | |
| rm -rf pkg/api/v1 | |
| mkdir -p pkg/api/v1 | |
| cp -v cloudnative-pg/api/v1/*_types.go pkg/api/v1 | |
| cp -v cloudnative-pg/api/v1/doc.go pkg/api/v1 | |
| cp -v cloudnative-pg/api/v1/groupversion_info.go pkg/api/v1 | |
| cp -v cloudnative-pg/api/v1/zz_*.go pkg/api/v1 | |
| cp -v cloudnative-pg/go.mod cloudnative-pg/go.sum . | |
| sed -i '1 s|^.*$|module github.com/cloudnative-pg/api|' go.mod | |
| go mod tidy | |
| go vet ./... | |
| git add pkg/api/v1 go.mod go.sum | |
| if git diff --cached --quiet; then | |
| echo "Upstream commit $sha has no net effect on synced files, skipping." | |
| continue | |
| fi | |
| GIT_AUTHOR_NAME=$(git -C cloudnative-pg show -s --pretty=%an "$sha") | |
| GIT_AUTHOR_EMAIL=$(git -C cloudnative-pg show -s --pretty=%ae "$sha") | |
| GIT_AUTHOR_DATE=$(git -C cloudnative-pg show -s --pretty=%aI "$sha") | |
| export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE | |
| # Qualify bare "#NNNN" references so they link to the upstream repo | |
| # rather than to issues/PRs in this mirror. The negative lookbehind | |
| # keeps already-qualified refs (e.g. owner/repo#7) and "word#7" untouched. | |
| MSG=$(git -C cloudnative-pg show -s --pretty=%B "$sha" \ | |
| | perl -pe 's{(?<![\w/])#(\d+)\b}{$ENV{UPSTREAM}#$1}g') | |
| git commit \ | |
| -m "$MSG" \ | |
| -m "(cherry picked from commit ${UPSTREAM}@${sha})" | |
| done | |
| # We want to prevent the "include administrators" branch protection from blocking the push, | |
| # but it should be usually there to avoid accidental pushes to main. | |
| - name: Temporarily disable "include administrators" branch protection | |
| if: ${{ github.ref == 'refs/heads/main' }} | |
| id: disable_include_admins | |
| uses: benjefferies/branch-protection-bot@v1.1.2 | |
| with: | |
| access_token: ${{ secrets.REPO_PAT }} | |
| branch: main | |
| enforce_admins: false | |
| - name: Push synced commits | |
| run: git push origin "HEAD:${GITHUB_REF_NAME}" | |
| - name: Enable "include administrators" branch protection | |
| uses: benjefferies/branch-protection-bot@v1.1.2 | |
| if: ${{ always() && github.ref == 'refs/heads/main' }} | |
| with: | |
| access_token: ${{ secrets.REPO_PAT }} | |
| branch: main | |
| enforce_admins: ${{ steps.disable_include_admins.outputs.initial_status }} | |
| # When the sync is triggered by a release, the cloudnative-pg release-tag | |
| # workflow passes the release tag in the dispatch payload. Tag the freshly | |
| # synced commit so the API repository keeps the same tags as cloudnative-pg. | |
| - name: Tag the synced release | |
| if: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.tag != '' }} | |
| env: | |
| TAG: ${{ github.event.client_payload.tag }} | |
| run: | | |
| if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then | |
| echo "Tag ${TAG} already exists, nothing to do." | |
| exit 0 | |
| fi | |
| git tag "${TAG}" | |
| git push origin "${TAG}" |