Skip to content

Release

Release #22

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
version_bump:
description: "Version bump type"
required: true
default: "patch"
type: choice
options:
- patch
- minor
- major
concurrency:
group: release
cancel-in-progress: true
permissions:
contents: write
jobs:
release:
name: Create release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_TOKEN }}
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Calculate new version
id: version
env:
BUMP_TYPE: ${{ inputs.version_bump }}
run: |
# Get the latest semver tag
LATEST_TAG=$(git tag -l 'v*.*.*' --sort=-v:refname | head -n1)
if [ -z "$LATEST_TAG" ]; then
echo "No existing version tags found, starting at v0.0.0"
MAJOR=0; MINOR=0; PATCH=0
else
VERSION="${LATEST_TAG#v}"
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
echo "Current version: v${MAJOR}.${MINOR}.${PATCH}"
fi
# Bump the chosen segment
case "$BUMP_TYPE" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac
NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
# Collision avoidance: if tag already exists, bump patch until unique
while git rev-parse "$NEW_VERSION" >/dev/null 2>&1; do
echo "Tag $NEW_VERSION already exists, bumping patch..."
PATCH=$((PATCH + 1))
NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
done
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "previous=${LATEST_TAG}" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION (previous: ${LATEST_TAG:-none})"
# CI cannot push commits to main (branch protection). Instead, we create
# a detached release commit with pinned refs, reachable only via tags.
# main keeps @latest refs for development; tagged commits are self-contained.
- name: Create release commit with pinned refs
id: release-commit
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
set -e
echo "Pinning @latest refs to ${VERSION}..."
# Pin docker/cagent-action@latest -> @vX.Y.Z
sed -i "s|docker/cagent-action@latest|docker/cagent-action@${VERSION}|g" review-pr/action.yml
# Pin docker/cagent-action/review-pr@latest -> @vX.Y.Z
sed -i "s|docker/cagent-action/review-pr@latest|docker/cagent-action/review-pr@${VERSION}|g" .github/workflows/review-pr.yml
# Verify pinning succeeded — fail fast if refs weren't replaced
if ! grep -q "docker/cagent-action@${VERSION}" review-pr/action.yml; then
echo "::error::Failed to pin refs in review-pr/action.yml"
exit 1
fi
if ! grep -q "docker/cagent-action/review-pr@${VERSION}" .github/workflows/review-pr.yml; then
echo "::error::Failed to pin refs in .github/workflows/review-pr.yml"
exit 1
fi
echo "Pinned refs:"
grep -n "cagent-action@" review-pr/action.yml .github/workflows/review-pr.yml
# Create a detached commit (not on main) with the pinned refs
# Note: write-tree captures the full index (all files from HEAD),
# but we explicitly add CAGENT_VERSION for clarity.
git add review-pr/action.yml .github/workflows/review-pr.yml CAGENT_VERSION
TREE=$(git write-tree)
RELEASE_SHA=$(git commit-tree "$TREE" -p HEAD -m "release: ${VERSION}")
echo "sha=$RELEASE_SHA" >> $GITHUB_OUTPUT
echo "Release commit: $RELEASE_SHA"
- name: Push version tag
env:
VERSION: ${{ steps.version.outputs.version }}
RELEASE_SHA: ${{ steps.release-commit.outputs.sha }}
run: |
git tag "$VERSION" "$RELEASE_SHA"
git push origin "$VERSION"
- name: Create GitHub Release
env:
VERSION: ${{ steps.version.outputs.version }}
PREVIOUS: ${{ steps.version.outputs.previous }}
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
ARGS=(--generate-notes --latest)
if [ -n "$PREVIOUS" ]; then
ARGS+=(--notes-start-tag "$PREVIOUS")
fi
gh release create "$VERSION" "${ARGS[@]}"
- name: Update latest tag
env:
VERSION: ${{ steps.version.outputs.version }}
RELEASE_SHA: ${{ steps.release-commit.outputs.sha }}
run: |
# Delete existing latest tag (local + remote)
git tag -d latest 2>/dev/null || true
git push origin :refs/tags/latest 2>/dev/null || true
# Create new latest tag pointing to the release commit
git tag latest "$RELEASE_SHA"
git push origin latest
echo "Updated 'latest' tag to point to ${VERSION} ($RELEASE_SHA)"
publish-agent:
name: Push review-pr agent to Docker Hub
needs: release
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.release.outputs.version }}
- name: Install cagent
run: |
set -e
CAGENT_VERSION=$(tr -d '[:space:]' < CAGENT_VERSION)
if [ -z "$CAGENT_VERSION" ]; then
echo "::error::Could not extract cagent version from CAGENT_VERSION"
exit 1
fi
echo "Using cagent version from CAGENT_VERSION: ${CAGENT_VERSION}"
curl -fL -o cagent \
"https://github.com/docker/cagent/releases/download/${CAGENT_VERSION}/cagent-linux-amd64"
chmod +x cagent
sudo mv cagent /usr/local/bin/
- name: Docker Hub login
env:
HUB_USER: ${{ secrets.HUB_USER }}
HUB_PAT: ${{ secrets.HUB_PAT }}
run: |
set -e
if [ -z "${HUB_USER}" ]; then echo "::error::HUB_USER secret is not set"; exit 1; fi
if [ -z "${HUB_PAT}" ]; then echo "::error::HUB_PAT secret is not set"; exit 1; fi
echo "${HUB_PAT}" | docker login --username "${HUB_USER}" --password-stdin
- name: Push agent
env:
HUB_ORG: ${{ secrets.HUB_ORG }}
run: |
set -e
if [ -z "${HUB_ORG}" ]; then echo "::error::HUB_ORG secret is not set"; exit 1; fi
cd review-pr/agents
TELEMETRY_ENABLED=false cagent share push pr-review.yaml "${HUB_ORG}/review-pr"
- name: Upload README to Docker Hub
env:
HUB_USER: ${{ secrets.HUB_USER }}
HUB_PAT: ${{ secrets.HUB_PAT }}
HUB_ORG: ${{ secrets.HUB_ORG }}
run: |
set -e
TOKEN=$(curl -sSf -X POST https://hub.docker.com/v2/users/login/ \
-H "Content-Type: application/json" \
-d "{\"username\":\"${HUB_USER}\",\"password\":\"${HUB_PAT}\"}" | jq -r .token)
if [ -z "${TOKEN}" ] || [ "${TOKEN}" = "null" ]; then
echo "::error::Failed to get Docker Hub API token"
exit 1
fi
curl -sSf -X PATCH "https://hub.docker.com/v2/namespaces/${HUB_ORG}/repositories/review-pr" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg desc "Docker Agent-powered PR review team. Analyzes code changes, posts reviews, and learns from feedback." \
--rawfile readme review-pr/README.md \
'{description: $desc, full_description: $readme}')"