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
130 changes: 130 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Release

on:
push:
tags:
- "v[0-9]*"

permissions:
contents: read

concurrency:
group: release
cancel-in-progress: false

jobs:
images:
name: Publish ${{ matrix.image }}
runs-on: ubuntu-latest
timeout-minutes: 90
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- image: agentkit
dockerfile: Dockerfile
- image: serve-pydantic-ai
dockerfile: runtimes/pydantic-ai/Dockerfile
- image: serve-maf
dockerfile: runtimes/microsoft-agent-framework/Dockerfile
- image: serve-langgraph
dockerfile: runtimes/langgraph/Dockerfile
steps:
- name: Validate release tag
env:
TAG: ${{ github.ref_name }}
run: |
if [[ "$TAG" == *+* ]]; then
echo "::error::Release tags must not contain build metadata (+...), which can collide with Docker tags."
exit 1
fi

- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Set up QEMU
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
with:
platforms: arm64

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

- name: Login to GHCR
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Image metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ghcr.io/${{ github.repository }}/${{ matrix.image }}
tags: type=semver,pattern={{raw}}
Comment thread
sozercan marked this conversation as resolved.
Comment thread
sozercan marked this conversation as resolved.
# Promote latest only after every image has published successfully.
flavor: latest=false

- name: Build and push
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: LDFLAGS=-X github.com/sozercan/agentkit/pkg/version.Version=${{ github.ref_name }}
cache-from: type=gha,scope=release-${{ matrix.image }}
cache-to: type=gha,scope=release-${{ matrix.image }},mode=max
sbom: true
provenance: true

release:
name: Promote images and create GitHub Release
needs: images
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1

- name: Login to GHCR
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Promote latest and create release with generated notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: |
args=(--latest=false)
if [[ "$TAG" == *-* ]]; then
args+=(--prerelease)
else
# shellcheck disable=SC2016
latest=$(gh api graphql -F owner="${GH_REPO%/*}" -F name="${GH_REPO#*/}" \
-f query='query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { latestRelease { tagName } } }' \
--jq '.data.repository.latestRelease.tagName // ""')
# Older stable releases may publish, but must not roll latest back.
if [[ -z "$latest" || "$(printf '%s\n' "$latest" "$TAG" | sort -V | tail -n 1)" == "$TAG" ]]; then
Comment on lines +117 to +121
for image in agentkit serve-pydantic-ai serve-maf serve-langgraph; do
docker buildx imagetools create \
--tag "ghcr.io/$GH_REPO/$image:latest" "ghcr.io/$GH_REPO/$image:$TAG"
done
args=(--latest)
fi
fi
gh release create "$TAG" --repo "$GH_REPO" --verify-tag \
--title "$TAG" --generate-notes "${args[@]}"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ workflow.
adapters, auth, request handling, and tool lifecycle.
- [`docs/agent-abi.md`](docs/agent-abi.md) — built `/agent/agent.yaml` contract.
- [`docs/development.md`](docs/development.md) — local development and CI.
- [`docs/release.md`](docs/release.md) — publishing images and package setup.
- [`docs/orka.md`](docs/orka.md) — Orka harness mode and AgentRuntime rendering.
- [`docs/architecture.md`](docs/architecture.md) — codebase architecture map for
contributors.
Expand Down
36 changes: 36 additions & 0 deletions docs/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Release AgentKit

After CI passes for the commit you want to release, push a version tag:

```sh
git tag v0.1.0
git push origin v0.1.0
```

The release workflow builds and publishes these images for `linux/amd64` and
`linux/arm64` under `ghcr.io/sozercan/agentkit`:

- `agentkit`, the BuildKit frontend.
- `serve-pydantic-ai`, the Pydantic AI runtime.
- `serve-maf`, the Microsoft Agent Framework runtime.
- `serve-langgraph`, the LangGraph runtime.

Each image gets the version tag, such as `v0.1.0`. After all images publish,
a stable release updates `latest` unless a newer stable release already exists.
Prerelease tags such as `v0.2.0-rc.1` publish versioned images without
changing `latest` and create a GitHub prerelease.
Build metadata such as `+build.1` is rejected before publishing to prevent
collisions between versioned Docker tags.

A GitHub Release with generated notes is created only after all four images
publish. Publishing uses the repository's `GITHUB_TOKEN`; no separate registry
secret is needed. Builds include SBOM and provenance attestations.

Release runs are serialized. Updates across the four `latest` tags are not
atomic; use version tags when you need a fixed release, and rerun a failed
workflow to finish an interrupted publication.

On first publication, GHCR packages default to private. After the first release,
open each of the four packages' settings and change its visibility to **Public**
before announcing it. For existing packages published manually, grant this
repository Actions access if needed. The workflow does not change package access.
Loading