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

apx release prepare

Validates schemas, lifecycle, version-line compatibility. Writes .apx-release.yaml manifest.

Submit

apx release submit

Clones canonical repo, creates branch apx/release/<api-id>/<version>, opens PR. Records PR metadata in manifest.

Finalize

apx release 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#

  1. You push a tag matching the APX pattern (e.g. proto/payments/ledger/v1/v1.0.0)

  2. The app repo’s apx-release.yml workflow triggers

  3. CI validates the schema and calls apx release prepare + apx release submit

  4. A PR is opened on the canonical repo

  5. Canonical CI validates the PR, reviewers approve, and it merges

  6. 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 (apx release prepare + submit)

CI (tag-triggered)

Trigger

Run from terminal

Push a git tag

Auth

Your gh CLI session

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):

  1. Canonical CI validates β€” ci.yml runs apx lint and apx breaking --against origin/main

  2. Review β€” team members review the schema changes

  3. Merge β€” PR merges to main

  4. Post-merge CI β€” on-merge.yml runs:

    • Re-validates schemas

    • Runs apx catalog generate to update catalog/catalog.yaml

    • Commits catalog changes with [skip ci]

See CI Templates for details on canonical repo workflows.


Prerequisites#

For Manual Releasing#

  • gh CLI 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_ID and APX_APP_PRIVATE_KEY available to the app repo

  • apx-release.yml workflow 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

experimental

-alpha.* prerelease

v1.0.0-alpha.1

beta

-alpha.*, -beta.*, or -rc.*

v1.0.0-beta.1

stable

No prerelease tag

v1.0.0

deprecated

Any

v1.2.1

sunset

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#