Release #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}')" |