Skip to content
Merged
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
187 changes: 187 additions & 0 deletions .github/workflows/sync-version-switcher.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
name: Sync version switcher
permissions: {}

# Triggered automatically when the `version:` field in params.yaml changes on
# main (i.e. a new release is cut), or manually for one-off runs.
on:
push:
branches: [main]
paths: ['config/_default/params.yaml']
workflow_dispatch:
inputs:
version:
description: 'Version label to add (e.g. v0.14)'
required: true
url:
description: 'URL for this version'
required: true
default: 'https://projectcapsule.dev'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
sync:
name: Sync version switcher
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Comment thread
sandert-k8s marked this conversation as resolved.
with:
fetch-depth: 0

- name: Determine version to sync
id: ver
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
echo "url=${{ inputs.url }}" >> "$GITHUB_OUTPUT"
else
# Use github.event.before as the reliable baseline for the push.
# HEAD~1 breaks when multiple commits land in a single push.
OLD=$(git show "${{ github.event.before }}":config/_default/params.yaml 2>/dev/null \
| grep '^version:' | awk '{print $2}' || echo "")
NEW=$(grep '^version:' config/_default/params.yaml | awk '{print $2}')
if [ "$OLD" = "$NEW" ]; then
echo "Version unchanged ($NEW), nothing to sync"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "version=$NEW" >> "$GITHUB_OUTPUT"
echo "url=$(grep '^url_latest_version:' config/_default/params.yaml | awk '{print $2}')" \
>> "$GITHUB_OUTPUT"
fi

- name: Open PRs for all release branches
if: steps.ver.outputs.skip != 'true'
env:
VERSION: ${{ steps.ver.outputs.version }}
URL: ${{ steps.ver.outputs.url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION_SLUG=$(echo "$VERSION" | sed 's/^v//;s/\./-/g')
REPO="${GITHUB_REPOSITORY}"

# Ensure all remote release branches are available locally
git fetch --prune origin

# Find all versioned release branches (release/0.x), skip preview branches
for branch in $(git branch -r | grep -E 'origin/release/[0-9]' | sed 's|.*origin/||'); do
echo "::group::$branch"
git checkout -B "$branch" "origin/$branch"

WORK_BRANCH="chore/add-${VERSION_SLUG}-to-${branch//\//-}"

# Determine whether to open a new PR or update an existing one
if git ls-remote --exit-code origin "refs/heads/${WORK_BRANCH}" > /dev/null 2>&1; then
PR_ACTION="update"
else
PR_ACTION="create"
fi

python3 - <<'PYEOF'
import os, re

version = os.environ['VERSION']
url = os.environ['URL']

with open('config/_default/params.yaml') as f:
content = f.read()

# Repoint any version still pointing to projectcapsule.dev to its
# frozen release subdomain (it was a previous latest, now archived).
def frozen_url(ver):
slug = ver.lstrip('v').replace('.', '-')
return f"https://release-{slug}.projectcapsule.dev"

content = re.sub(
r'^(- version: (v[\d.]+)\n url: https://projectcapsule\.dev)$',
lambda m: f"- version: {m.group(2)}\n url: {frozen_url(m.group(2))}",
content,
flags=re.MULTILINE,
)

# Insert the new version only if not already present (idempotent).
if not re.search(r'^- version: ' + re.escape(version) + r'$', content, re.MULTILINE):
new_entry = f"- version: {version}\n url: {url}\n"
content = re.sub(
r'(^versions:\n)',
r'\g<1>' + new_entry,
content,
flags=re.MULTILINE,
count=1,
)

with open('config/_default/params.yaml', 'w') as f:
f.write(content)
PYEOF

# Check if there are actual changes vs the release branch HEAD
if git diff --quiet HEAD -- config/_default/params.yaml; then
echo "Already in desired state, skipping ${branch}"
git restore config/_default/params.yaml
echo "::endgroup::"
continue
fi

# Save desired content to a temp file (preserves trailing newline)
DESIRED_FILE=$(mktemp)
cat config/_default/params.yaml > "$DESIRED_FILE"
git restore config/_default/params.yaml

# --- API-based commit (GitHub auto-verifies; no GPG key needed) ---

# Get the release branch HEAD and its tree
BASE_SHA=$(gh api "repos/${REPO}/branches/${branch}" --jq '.commit.sha')
BASE_TREE_SHA=$(gh api "repos/${REPO}/git/commits/${BASE_SHA}" --jq '.tree.sha')

# Upload the modified file as a blob
CONTENT_B64=$(base64 -w 0 < "$DESIRED_FILE")
rm -f "$DESIRED_FILE"
BLOB_SHA=$(gh api "repos/${REPO}/git/blobs" \
-f content="$CONTENT_B64" \
-f encoding="base64" \
--jq '.sha')

# Create a new tree with the updated file
NEW_TREE_SHA=$(jq -n \
--arg base_tree "$BASE_TREE_SHA" \
--arg blob "$BLOB_SHA" \
'{base_tree:$base_tree,tree:[{path:"config/_default/params.yaml",mode:"100644",type:"blob",sha:$blob}]}' | \
gh api "repos/${REPO}/git/trees" --input - --jq '.sha')

# Build commit message with Signed-off-by for DCO
COMMIT_MSG=$(printf 'chore: add %s to version switcher in %s\n\nSigned-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>' \
"$VERSION" "$branch")

# Create the commit (GitHub signs API commits automatically → Verified)
NEW_COMMIT_SHA=$(jq -n \
--arg msg "$COMMIT_MSG" \
--arg tree "$NEW_TREE_SHA" \
--arg parent "$BASE_SHA" \
'{message:$msg,tree:$tree,parents:[$parent]}' | \
gh api "repos/${REPO}/git/commits" --input - --jq '.sha')

# Create or force-update the work branch ref
if [ "$PR_ACTION" = "update" ]; then
gh api "repos/${REPO}/git/refs/heads/${WORK_BRANCH}" \
-X PATCH -f sha="$NEW_COMMIT_SHA" -F force=true
echo "Updated PR branch: ${WORK_BRANCH}"
else
gh api "repos/${REPO}/git/refs" \
-f ref="refs/heads/${WORK_BRANCH}" \
-f sha="$NEW_COMMIT_SHA"
gh pr create \
--repo "$REPO" \
--base "$branch" \
--head "$WORK_BRANCH" \
--title "chore: add ${VERSION} to version switcher in ${branch}" \
--body "Automated PR: adds \`${VERSION}\` to the version switcher in the \`${branch}\` release docs. Triggered by: ${{ github.event_name }} (${{ github.sha }})"
echo "Opened PR: ${WORK_BRANCH} -> ${branch}"
fi

echo "::endgroup::"
done
8 changes: 8 additions & 0 deletions config/_default/hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,11 @@ menu:
url: /project/
pre: <i class='fas fa-flask'></i>
weight: 18

# Disable the file-system cache for remote resources (resources.GetRemote).
# Hugo's default is maxAge: -1 (never expire), which causes stale Helm index
# responses to persist across Netlify builds. Setting maxAge to 0 forces a
# fresh fetch on every build so chart versions are always current.
caches:
getresource:
maxAge: 0
6 changes: 2 additions & 4 deletions config/_default/params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ version_menu: "Releases"
# The version number for the version of the docs represented in this doc set.
# Used in the "version-banner" partial to display a version number for the
# current doc set.
version: latest
version: v0.13
Comment thread
sandert-k8s marked this conversation as resolved.
url_latest_version: https://projectcapsule.dev
archived_version: false
versions:
- version: v0.13
url: https://release-0-13.projectcapsule.dev
url: https://projectcapsule.dev
- version: v0.12
url: https://release-0-12.projectcapsule.dev
- version: v0.11
url: https://release-0-11.projectcapsule.dev
- version: v0.10
url: https://release-0-10.projectcapsule.dev
- version: latest
url: https://projectcapsule.dev
Loading