Skip to content

sync-api

sync-api #1887

Workflow file for this run

# 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}"