Release Workflow#
This page covers how to release schemas from an app repository to the canonical repository. APX provides a multi-step release pipeline and supports both manual and CI-triggered workflows.
Overview#
Releasing moves a schema snapshot from your app repo into the canonical repository via a pull request. The canonical repoβs CI then validates, tags, and catalogs the release.
βββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
β App Repository β β Canonical Repository β
β β β β
β 1. Author schemas β β β
β 2. Validate locally β β β
β 3. Tag or run release β β β
β β PR β β
β 4. apx release prepare β β β
β apx release submit ββββββββββΊ β 5. CI validates PR β
β β β 6. Reviewer approves β
β β β 7. Merge to main β
β β β 8. Post-merge CI: β
β β β - catalog update β
β β β - tag creation β
βββββββββββββββββββββββββββ ββββββββββββββββββββββββββββ
Release Pipeline (apx release)#
A multi-step workflow with explicit phases, manifest persistence, and idempotency checks. Works for both local development and CI pipelines.
# Step 1: Validate and create a release manifest
apx release prepare proto/payments/ledger/v1 --version v1.0.0
# Step 2: Push release branch and open PR on canonical repo
apx release submit
# Step 3: Tag, catalog, and emit release record (run by canonical CI)
apx release finalize
What each step does:
Step |
Command |
Output |
|---|---|---|
Prepare |
|
Validates schemas, lifecycle, version-line compatibility. Writes |
Submit |
|
Clones canonical repo, creates branch |
Finalize |
|
Re-validates, creates annotated tag, updates catalog, emits immutable release record. Run by canonical CI after merge. |
The release pipeline also supports lifecycle promotions:
apx release promote proto/payments/ledger/v1 --to stable --version v1.0.0
apx release submit
# Examples
apx release prepare proto/payments/ledger/v1 --version v1.0.0-alpha.1 --lifecycle experimental
apx release prepare proto/payments/ledger/v1 --version v1.0.0-beta.1 --lifecycle beta
apx release prepare proto/payments/ledger/v1 --version v1.0.0 --lifecycle stable
# Preview without releasing
apx release prepare proto/payments/ledger/v1 --version v1.0.0 --dry-run
See Release Commands for full reference.
CI-Triggered Releasing#
The recommended production workflow uses CI to release automatically when you push a tag.
How It Works#
You push a tag matching the APX pattern (e.g.
proto/payments/ledger/v1/v1.0.0)The app repoβs
apx-release.ymlworkflow triggersCI validates the schema and calls
apx release prepare+apx release submitA PR is opened on the canonical repo
Canonical CI validates the PR, reviewers approve, and it merges
Post-merge CI on the canonical repo creates the official tag and updates the catalog
Tag Format#
Tags follow the pattern <api-id>/<version>:
proto/payments/ledger/v1/v1.0.0
openapi/billing/invoices/v2/v2.1.0-beta.1
avro/events/clicks/v1/v1.0.0-alpha.3
Triggering a Release#
# Create and push a tag
git tag proto/payments/ledger/v1/v1.0.0
git push origin proto/payments/ledger/v1/v1.0.0
The apx-release.yml workflow matches tags against these patterns:
on:
push:
tags:
- "proto/**/v[0-9]*"
- "openapi/**/v[0-9]*"
- "avro/**/v[0-9]*"
- "jsonschema/**/v[0-9]*"
- "parquet/**/v[0-9]*"
The App Repo Workflow#
The apx-release.yml workflow is generated by apx init app or apx workflows sync:
name: APX Release
on:
push:
tags:
- "proto/**/v[0-9]*"
- "openapi/**/v[0-9]*"
- "avro/**/v[0-9]*"
- "jsonschema/**/v[0-9]*"
- "parquet/**/v[0-9]*"
permissions:
contents: read
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Generate App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APX_APP_ID }}
private-key: ${{ secrets.APX_APP_PRIVATE_KEY }}
owner: <org>
repositories: <canonical-repo>
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install APX
uses: infobloxopen/apx@v1
- name: Parse tag
id: tag
run: |
TAG="${GITHUB_REF#refs/tags/}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Validate
run: |
apx lint
apx breaking --against HEAD^ || true
- name: Extract API ID and version from tag
id: parse
run: |
TAG="${{ steps.tag.outputs.tag }}"
# Tag format: <api-id>/<version> e.g. proto/payments/ledger/v1/v1.0.0
VERSION="${TAG##*/}" # last component
API_ID="${TAG%/*}" # everything before last /
echo "api_id=${API_ID}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Release to canonical repo
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
apx release prepare "${{ steps.parse.outputs.api_id }}" \
--version "${{ steps.parse.outputs.version }}" \
--canonical-repo=github.com/<org>/<canonical-repo>
apx release submit
The <org> and <canonical-repo> placeholders are filled from your apx.yaml when the workflow is generated.
Manual vs CI Releasing#
Aspect |
Manual ( |
CI (tag-triggered) |
|---|---|---|
Trigger |
Run from terminal |
Push a git tag |
Auth |
Your |
GitHub App token |
Validation |
Local only |
CI runs lint + breaking |
Audit trail |
Git log |
CI run URL in PR body |
Best for |
Development, quick iteration |
Production releases |
Both paths produce the same result: a PR on the canonical repo with the schema snapshot.
What Happens After the PR#
Once the PR is opened on the canonical repo (by either path):
Canonical CI validates β
ci.ymlrunsapx lintandapx breaking --against origin/mainReview β team members review the schema changes
Merge β PR merges to
mainPost-merge CI β
on-merge.ymlruns:Re-validates schemas
Runs
apx catalog generateto updatecatalog/catalog.yamlCommits catalog changes with
[skip ci]
See CI Templates for details on canonical repo workflows.
Prerequisites#
For Manual Releasing#
ghCLI installed and authenticated (gh auth login)Write access to the canonical repo (push branches + create PRs)
For CI Releasing#
APX GitHub App installed on both the app repo and canonical repo
Org secrets
APX_APP_IDandAPX_APP_PRIVATE_KEYavailable to the app repoapx-release.ymlworkflow in.github/workflows/
See Protection for GitHub App setup.
Lifecycle and Version Rules#
APX enforces consistency between lifecycle state and version:
Lifecycle |
Required version format |
Example |
|---|---|---|
|
|
|
|
|
|
|
No prerelease tag |
|
|
Any |
|
|
Blocked (no new releases) |
β |
# These work:
apx release prepare proto/payments/ledger/v1 --version v1.0.0-alpha.1 --lifecycle experimental
apx release prepare proto/payments/ledger/v1 --version v1.0.0 --lifecycle stable
# This fails (stable requires clean semver):
apx release prepare proto/payments/ledger/v1 --version v1.0.0-beta.1 --lifecycle stable
See Release Overview for the full lifecycle model.
Troubleshooting#
apx release prepare fails with βgh: not authenticatedβ#
Run gh auth login to authenticate the GitHub CLI.
PR creation fails with permission error#
Ensure you have push access to the canonical repo, or use the CI-triggered path with the GitHub App token.
βversion already releasedβ error#
The same version with identical content was already released. This is an idempotency check β apx release prepare detects the existing tag by comparing SHA-256 content hashes and reports success.
CI workflow doesnβt trigger on tag push#
Verify the tag matches the expected pattern. Tags must start with the schema format prefix:
# Correct
git tag proto/payments/ledger/v1/v1.0.0
# Wrong β missing the version line directory
git tag proto/payments/ledger/v1.0.0
Next Steps#
CI Integration β app repo CI setup details
Release Commands β multi-step pipeline
CI Templates β canonical repo workflows