Skip to content

Release Versioning

How soa images are versioned, tagged, and auto-deployed to staging.

Tag scheme

Every successful master push that follows the conventional commit format produces one GH release and three DOCR image tags:

Tag Example Purpose
v<semver> v1.3.0 Primary deploy tag. Used by do-deploy.yml.
<short-sha> abc1234 Traceability. Points to exact commit.
latest latest Convenience. Always newest master build.

workflow_dispatch rebuilds (using the ref input) push only <short-sha> - no semver, no latest.

Bump rules

Conventional commit messages since the last semver tag determine the bump:

Commit prefix Bump Example
feat: / feat(<scope>): minor v1.2.0 -> v1.3.0
BREAKING CHANGE in body, or ! in type major v1.2.0 -> v2.0.0
Everything else (fix:, chore:, ci:, etc.) patch v1.2.0 -> v1.2.1

Policy: every master push with a conventional commit type produces a release. Commits without a recognized type prefix (e.g. bare "Initial commit") do not trigger a release - they push a <short-sha> image only, with no version bump.

Baseline

First release uses v1.0.0. No existing semver tags existed before BD-5.2 was implemented.

How it works inside do-build.yml

push master
  -> validate-jsonnet
  -> version job        (semantic-release dry_run=true: compute v<semver>, no tag yet)
  -> bake               (build image tagged v<semver>)
  -> lint + test + trivy
  -> push               (push v<semver> + short-sha + latest to DOCR)
  -> sentry             (create Sentry release named v<semver>)
  -> release            (semantic-release real run: create git tag + GH release,
                          then dispatch do-deploy oep-stg)
  -> prune              (keep newest 30 DOCR tags by count)

The release job runs after push succeeds. semantic-release dry-run in the version job computes the next version without creating the tag - the real tag + GH release is only created once the image is confirmed pushed. This guarantees no orphaned git tags for failed builds.

Versioning is driven by .releaserc.json at the soa repo root using cycjimmy/semantic-release-action@v4.

oep-stg auto-deploy

After creating the GH release, the release job automatically dispatches do-deploy.yml for oep-stg:

gh workflow run do-deploy.yml --field tag=v<semver> --field tenant=oep-stg

oep-stg has no approval gate - it deploys immediately.

Prod tenants (mansety-prd, us-prd) are never auto-deployed. Operator must dispatch manually and the Backend team approval gate fires before proceed.

See Promote Image for manual deploy steps.

Dispatch rebuild (no semver)

Rebuilding an old commit via workflow_dispatch (using the ref input) does NOT produce a new semver or GH release. It pushes only <short-sha> to DOCR. This is intentional - rebuilds are for recovering a specific commit's image, not releasing new code.

# Rebuild image from a specific commit or tag
gh workflow run do-build.yml -R unipuka/soa -f ref=abc1234
# or
gh workflow run do-build.yml -R unipuka/soa -f ref=v1.2.3

Bugsnag release integration

PHP has no sourcemaps; Bugsnag "release" = a build report sent to build.bugsnag.com. This is posted per-tenant per-deploy from do-deploy.yml (after the Sentry deploy marker step).

Canonical version: full commit SHA

Both Sentry and Bugsnag use the full COMMIT_SHA baked into the image at build time as the canonical version identifier. This ensures errors in Sentry and Bugsnag both point to the same image + commit.

Layer Value
Runtime (config/sentry.php) COMMIT_SHA env (full SHA)
Runtime (config/bugsnag.php) BUGSNAG_APP_VERSION falling back to COMMIT_SHA
Sentry CI (do-build.yml) v{semver} or 7-char SHA
Bugsnag CI (do-deploy.yml) Full SHA resolved from deploy tag

Per-tenant project keys

Each tenant has a separate Bugsnag project. The BUGSNAG_API_KEY secret is scoped to each GitHub Environment (oep-stg, mansety-prd, us-prd) so do-deploy.yml picks up the correct key automatically.

Tenant Bugsnag API key GH Environment
oep-stg see Doppler oep/oep-stgBUGSNAG_API_KEY oep-stg
mansety-prd see Doppler oep/mansety-prdBUGSNAG_API_KEY mansety-prd
us-prd see Doppler oep/us-prdBUGSNAG_API_KEY us-prd

Release stage mapping

do-deploy.yml maps tenant -> releaseStage for the build report:

Tenant Release stage
oep-stg staging
mansety-prd, us-prd production

This must match config/bugsnag.php release_stage (defaults to APP_ENV).

V&V

  1. Trigger a deploy: gh workflow run do-deploy.yml -R unipuka/soa -f tag=v1.0.2 -f tenant=oep-stg
  2. Bugsnag dashboard -> Releases -> filter by stage staging -> release with appVersion = full SHA appears
  3. Trigger a deliberate exception on oep-stg only (staging; tinker may be unavailable on production images): kubectl -n oep-stg exec deploy/oep-stg-laravel-api -- php artisan tinker --execute="throw new \Exception('smoke-test');" -> Bugsnag captures the error with the correct appVersion (matching the deployed build's SHA). For production tenants, use a dedicated /smoke-error test route if available, or verify via a naturally occurring exception rather than triggering one.

Checking releases

# List recent GH releases
gh release list -R unipuka/soa -L 10

# List DOCR tags for the api repo
doctl registry repository list-tags api

# Show what tag is deployed per tenant
cat apps/laravel/values-oep-stg.yaml | grep 'image.tag\|tag:'
cat apps/laravel/values-mansety-prd.yaml | grep 'image.tag\|tag:'