name: Sync Docker release branch concurrency: group: ${{ github.workflow }}-${{ inputs.release_branch }} cancel-in-progress: false permissions: contents: read on: workflow_dispatch: inputs: release_branch: description: Release branch to sync, for example 29.x required: true type: string tag: description: Tag to sync from, for example v29.6.0 required: true type: string dry_run: description: Merge but don't push required: true default: false type: boolean jobs: sync-release-branch: runs-on: ubuntu-26.04 permissions: contents: write actions: write outputs: base_sha: ${{ steps.sync.outputs.base_sha }} has_changes: ${{ steps.sync.outputs.has_changes }} temporary_branch: ${{ steps.sync.outputs.temporary_branch }} temporary_sha: ${{ steps.sync.outputs.temporary_sha }} timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Validate env: BRANCH: ${{ github.ref_name }} RELEASE_BRANCH: ${{ inputs.release_branch }} run: | if [ "$BRANCH" != "master" ]; then echo "::error::This workflow is expected to be run on master, not $BRANCH" exit 1 fi if [ "$RELEASE_BRANCH" = "master" ]; then echo "::error::The release branch must not be master" exit 1 fi if ! [[ "$RELEASE_BRANCH" =~ ^[0-9]+\.[x0-9]+$ ]]; then echo "::error::Invalid release branch name: '$RELEASE_BRANCH'. Expected format: 29.x" exit 1 fi - name: Configure git author run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Sync release branch to tag range id: sync env: DRY_RUN: ${{ inputs.dry_run }} RELEASE_BRANCH: ${{ inputs.release_branch }} RUN_ATTEMPT: ${{ github.run_attempt }} RUN_ID: ${{ github.run_id }} TAG: ${{ inputs.tag }} run: | set -o pipefail base_sha=$(git rev-parse "origin/$RELEASE_BRANCH") temporary_branch="process/sync-release-branch/$RUN_ID-$RUN_ATTEMPT" echo "base_sha=$base_sha" >> "$GITHUB_OUTPUT" echo "temporary_branch=$temporary_branch" >> "$GITHUB_OUTPUT" # Keep the master checkout unchanged so scripts run from the dispatched revision. release_worktree="$RUNNER_TEMP/release-branch" git worktree add --detach "$release_worktree" "$base_sha" cd "$release_worktree" tags_file=$(mktemp) "$GITHUB_WORKSPACE/scripts/unmerged-tags" \ "origin/$RELEASE_BRANCH" \ "$TAG" \ > "$tags_file" echo >> "$GITHUB_STEP_SUMMARY" echo "## Tags to sync" >> "$GITHUB_STEP_SUMMARY" echo >> "$GITHUB_STEP_SUMMARY" sed 's/^/- /' "$tags_file" >> "$GITHUB_STEP_SUMMARY" xargs -r "$GITHUB_WORKSPACE/scripts/sync-branch" < "$tags_file" | tee -a "$GITHUB_STEP_SUMMARY" if [[ "$DRY_RUN" == "true" ]]; then echo "has_changes=false" >> "$GITHUB_OUTPUT" exit 0 fi if [[ $(git rev-parse HEAD) == $(git rev-parse "origin/$RELEASE_BRANCH") ]]; then echo "has_changes=false" >> "$GITHUB_OUTPUT" echo "No changes to push" exit 0 fi echo "has_changes=true" >> "$GITHUB_OUTPUT" git push origin "HEAD:refs/heads/$temporary_branch" echo "temporary_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" push-release-branch: needs: sync-release-branch if: ${{ !inputs.dry_run && needs.sync-release-branch.outputs.has_changes == 'true' }} runs-on: ubuntu-26.04 environment: docker-releases permissions: contents: write actions: write timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Push release branch env: BASE_SHA: ${{ needs.sync-release-branch.outputs.base_sha }} RELEASE_BRANCH: ${{ inputs.release_branch }} TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }} TEMPORARY_SHA: ${{ needs.sync-release-branch.outputs.temporary_sha }} run: | git fetch origin "$RELEASE_BRANCH" current_sha=$(git rev-parse "origin/$RELEASE_BRANCH") if [[ "$current_sha" != "$BASE_SHA" ]]; then echo "$RELEASE_BRANCH changed from $BASE_SHA to $current_sha" exit 1 fi git fetch origin "$TEMPORARY_BRANCH" current_temporary_sha=$(git rev-parse FETCH_HEAD) if [[ "$current_temporary_sha" != "$TEMPORARY_SHA" ]]; then echo "$TEMPORARY_BRANCH changed from $TEMPORARY_SHA to $current_temporary_sha" exit 1 fi git push origin "FETCH_HEAD:$RELEASE_BRANCH" - name: Delete temporary branch env: TEMPORARY_BRANCH: ${{ needs.sync-release-branch.outputs.temporary_branch }} run: git push origin --delete "$TEMPORARY_BRANCH"