Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 171 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ on:
branches: [main]

# contents:write is required for autotag-from-changelog to push tags.
# pull-requests:write is required for retargeting PRs to pending deploy branches.
permissions:
contents: write
pull-requests: write

jobs:
release:
runs-on: ubuntu-latest
outputs:
tag-created: ${{ steps.autotag.outputs.tag-created }}
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -28,10 +32,176 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CCLOUD_PRIVATE_DISPATCH_PAT }}
run: |
source scripts/lib/logging.sh

if [ -z "$GH_TOKEN" ]; then
echo "::warning::CCLOUD_PRIVATE_DISPATCH_PAT secret is not set. Skipping dispatch to ccloud-private for ${{ steps.autotag.outputs.tag }}."
log_warning "CCLOUD_PRIVATE_DISPATCH_PAT secret is not set. Skipping dispatch to ccloud-private for ${{ steps.autotag.outputs.tag }}."
exit 0
fi
gh api repos/cockroachdb/ccloud-private/dispatches \
-f event_type=sdk-release \
-f "client_payload[version]=${{ steps.autotag.outputs.tag }}"

# After a successful release, this job ensures there's always a fresh pending deploy
# branch ready for the next release cycle. It:
# 1. Finds the most recently merged pending deploy branch (from PRs to main)
# 2. Finds any existing live pending deploy branches
# 3. Creates a new pending deploy branch if there are no live ones or if the live
# ones are stale (associated with an older date than the last merged one)
# 4. Retargets all open PRs to point to the current pending deploy branch
manage-pending-deploy:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you comment a high level overview of how this works here? The Step comments are helpful but they feel piecemeal and it's hard to get an overall understanding of what we're doing here by following them.

needs: release
if: needs.release.outputs.tag-created == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

# Step 1: Determine the previous pending deploy branch timestamp from main
# Find the most recent merged PR to main where the head branch was automation/pending-deploy-YYYYMMDD-hhmmss
- name: Find previous pending deploy branch from merged PRs
id: previous
env:
GH_TOKEN: ${{ github.token }}
run: |
source scripts/lib/logging.sh

log_info "Searching for previous pending deploy branch from merged PRs..."

# Fetch last 50 merged PRs to find the most recent pending deploy branch.
# 50 should be sufficient given typical API change and SDK release cadence.
previous_branch=$(gh pr list \
--state merged \
--base main \
--limit 50 \
--json headRefName,mergedAt \
--jq '[.[] | select(.headRefName | test("^automation/pending-deploy-[0-9]{8}-[0-9]{6}$"))] | sort_by(.mergedAt) | reverse | .[0].headRefName')

if [ -z "$previous_branch" ] || [ "$previous_branch" = "null" ]; then
log_info "No previous pending deploy branch found from merged PRs"
echo "previous_branch_timestamp=" >> "$GITHUB_OUTPUT"
else
log_info "Found previous pending deploy branch: $previous_branch"

# Extract timestamp from the branch name: automation/pending-deploy-YYYYMMDD-hhmmss
previous_branch_timestamp=$(echo "$previous_branch" | sed -E 's/^automation\/pending-deploy-([0-9]{8}-[0-9]{6})$/\1/' | tr -d '-')

echo "previous_branch_timestamp=$previous_branch_timestamp" >> "$GITHUB_OUTPUT"

log_info " Timestamp: $previous_branch_timestamp"
fi

# Step 2: Determine the current live pending deploy branch in the main repository
# Look for live remote branches matching automation/pending-deploy-YYYYMMDD-hhmmss
# If multiple exist, select the one with the latest timestamp
- name: Find current live pending deploy branch
id: current
run: |
source scripts/lib/logging.sh

log_info "Searching for live pending deploy branches in the main repository..."

# List all remote branches matching the pattern, sorted by timestamp descending
current_branch=$(git ls-remote --heads origin | \
grep -oE 'automation/pending-deploy-[0-9]{8}-[0-9]{6}$' | \
sort -r | \
head -n 1)

if [ -z "$current_branch" ]; then
log_info "No live pending deploy branch found"
echo "current_branch=" >> "$GITHUB_OUTPUT"
echo "current_branch_timestamp=" >> "$GITHUB_OUTPUT"
else
log_info "Found current live pending deploy branch: $current_branch"

# Extract timestamp
current_branch_timestamp=$(echo "$current_branch" | sed -E 's/^automation\/pending-deploy-([0-9]{8}-[0-9]{6})$/\1/' | tr -d '-')

echo "current_branch=$current_branch" >> "$GITHUB_OUTPUT"
echo "current_branch_timestamp=$current_branch_timestamp" >> "$GITHUB_OUTPUT"

log_info " Timestamp: $current_branch_timestamp"
fi

# Step 3: Decide which pending deploy branch should be used now
# If previous timestamp >= current timestamp, create a new branch
- name: Select pending deploy branch
id: selected
env:
PREVIOUS_BRANCH_TIMESTAMP: ${{ steps.previous.outputs.previous_branch_timestamp }}
CURRENT_BRANCH: ${{ steps.current.outputs.current_branch }}
CURRENT_BRANCH_TIMESTAMP: ${{ steps.current.outputs.current_branch_timestamp }}
run: |
source scripts/lib/logging.sh

# Get current UTC timestamp in YYYYMMDD-hhmmss format
current_timestamp=$(date -u +%Y%m%d-%H%M%S)
log_info "Current UTC timestamp: $current_timestamp"

# Decision logic: create new branch if previous >= current, otherwise use current
if [ -z "$CURRENT_BRANCH" ]; then
# No current live branch exists, create new
log_info "No current live branch exists, creating new branch..."
selected_branch="automation/pending-deploy-${current_timestamp}"
needs_creation=true
elif [ -n "$PREVIOUS_BRANCH_TIMESTAMP" ] && [ "$PREVIOUS_BRANCH_TIMESTAMP" -ge "$CURRENT_BRANCH_TIMESTAMP" ]; then
# Previous timestamp >= current timestamp, create new branch
log_info "Previous timestamp ($PREVIOUS_BRANCH_TIMESTAMP) >= current timestamp ($CURRENT_BRANCH_TIMESTAMP), creating new branch..."
selected_branch="automation/pending-deploy-${current_timestamp}"
needs_creation=true
else
# Use current live branch
log_info "Using current live branch"
selected_branch="$CURRENT_BRANCH"
needs_creation=false
fi

log_info "Selected pending deploy branch: $selected_branch"
log_info "Needs creation: $needs_creation"

echo "selected_branch=$selected_branch" >> "$GITHUB_OUTPUT"
echo "needs_creation=$needs_creation" >> "$GITHUB_OUTPUT"

# Step 4: Ensure the selected pending deploy branch exists in the main repository
# Create it from main if it doesn't already exist remotely
- name: Create pending deploy branch if needed
if: steps.selected.outputs.needs_creation == 'true'
env:
SELECTED_BRANCH: ${{ steps.selected.outputs.selected_branch }}
run: |
source scripts/lib/logging.sh

log_info "Creating pending deploy branch: $SELECTED_BRANCH"

# Create the branch from main and push it to the main repository
git checkout -b "$SELECTED_BRANCH" origin/main
git push origin "$SELECTED_BRANCH"

log_info "Branch $SELECTED_BRANCH created and pushed to origin"

# Step 5: Retarget open PRs from any pending deploy branch to the selected branch
- name: Retarget open PRs
env:
GH_TOKEN: ${{ github.token }}
SELECTED_BRANCH: ${{ steps.selected.outputs.selected_branch }}
run: |
source scripts/lib/logging.sh

log_info "Looking for open PRs targeting pending deploy branches..."

# Find all open PRs and filter for those targeting automation/pending-deploy-YYYYMMDD-hhmmss branches
prs_to_retarget=$(gh pr list --state open --json number,baseRefName --jq '.[] | select(.baseRefName | test("^automation/pending-deploy-[0-9]{8}-[0-9]{6}$")) | select(.baseRefName != "'$SELECTED_BRANCH'") | "\(.number):\(.baseRefName)"')

if [ -z "$prs_to_retarget" ]; then
log_info "No open PRs found targeting pending deploy branches"
else
log_info "Found PRs to retarget to $SELECTED_BRANCH:"
echo "$prs_to_retarget" | while IFS=: read -r pr_number base_branch; do
log_info " PR #$pr_number (from $base_branch)"
gh pr edit "$pr_number" --base "$SELECTED_BRANCH"
log_info " Retargeted PR #$pr_number to $SELECTED_BRANCH"
done

log_info "All PRs retargeted successfully"
fi
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added pending deploy branch management to release workflow to ensure automated
PRs that remain open after an SDK release are retargeted to the latest pending
deploy branch

## [7.1.0] - 2026-04-14

### Added
Expand Down
27 changes: 27 additions & 0 deletions scripts/lib/logging.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Logging functions for GitHub Actions workflows

# Output an informational message to stdout
log_info() {
local message="$1"
echo "$message"
}

# Output an error message using GitHub Actions workflow command format
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message
log_error() {
local message="$1"
echo "::error::$message" >&2
}

# Output a warning message using GitHub Actions workflow command format
log_warning() {
local message="$1"
echo "::warning::$message" >&2
}

# Output a notice message using GitHub Actions workflow command format
log_notice() {
local message="$1"
echo "::notice::$message"
}
Loading