Contentrain Studio ships as an application image used for self-hosted and managed deployments, not as an npm package.
The release source of truth is:
- a semver Git tag
- a GitHub Release
- a Docker image published to
ghcr.io/contentrain/studio
Studio is currently in the 0.x line. Use semver honestly:
- first public testing cut:
v0.1.0-beta.1 - subsequent testing cuts:
v0.1.0-beta.2,v0.1.0-beta.3 - first stable cut:
v0.1.0
Use prerelease tags for user-testing and validation rounds. Do not move latest on prereleases.
Published artifact:
- Docker image:
ghcr.io/contentrain/studio
Not published from this repository:
- npm package
- public SDK package
- reusable UI library package
If Studio later exposes a publishable SDK or CLI, that should be extracted as a separate package boundary instead of publishing the root app.
Studio is trunk-based (see CONTRIBUTING.md → Branch Model). Every merge to main is deployed automatically to staging.contentrain.io by Railway, so pre-release verification happens on that environment before a tag is cut — not on a separate branch.
Before tagging, confirm on staging.contentrain.io:
- Auth flows (login, OAuth, magic link)
- Workspace and project list load
- GitHub App install and repo connection
- A full chat/content save round-trip
- Billing/media/CDN/forms surfaces applicable to this release
If staging is healthy, proceed to the local release flow below.
Before cutting a tag, make sure:
- You are on
mainand it is up to date withorigin/main mainis clean (no uncommitted changes)package.jsonhas the intended versionstaging.contentrain.iois running the exactmainHEAD you are about to tag
Run the local release gate:
pnpm release:checkThis runs:
pnpm lintpnpm typecheckpnpm test:cipnpm test:rlspnpm test:e2epnpm build
Then cut the release:
pnpm releasepnpm release is the controlled local entry point. It runs the full release gate first, then runs changelogen --release to update changelog and release metadata.
After review, push the release commit and tag:
git push origin main --follow-tagsThat tag push is what triggers the automated release pipeline.
The release pipeline lives in ../.github/workflows/release.yml.
Triggers:
pushon tags matchingv*workflow_dispatchfor republishing an existing tag manually
Pipeline steps:
- Checkout the tagged source
- Validate that
package.jsonversion matches the tag - Install dependencies with
pnpm install --frozen-lockfile - Generate the Contentrain client with
npx contentrain generate - Run lint, typecheck, tests, and build
- Build and push the Docker image to GHCR
- Create or update the GitHub Release entry
Every release publishes:
- exact tag:
ghcr.io/contentrain/studio:vX.Y.Z - exact prerelease tag:
ghcr.io/contentrain/studio:vX.Y.Z-beta.N - immutable commit tag:
ghcr.io/contentrain/studio:sha-<shortsha>
Stable releases additionally publish:
ghcr.io/contentrain/studio:X.Yghcr.io/contentrain/studio:Xghcr.io/contentrain/studio:latest
Prereleases do not publish latest.
If a release includes database or RLS changes:
- apply migrations first
- deploy the new image
- run smoke checks
- promote traffic
Do not deploy a new image that depends on unapplied schema changes.
Rollback strategy:
- identify the previous healthy image tag
- redeploy that exact image tag
- if needed, roll forward with a fix release instead of mutating old tags
Do not retag old images or reuse a published semver tag for different bits.
After a release deploy:
/api/healthreturns200- login and callback flows work
- workspace list and project load work
- GitHub installation and repo connection work
- one chat/content change flow completes
- billing/media/CDN/forms surfaces behave according to the configured environment
Before cutting the first public beta:
- merge the release automation and docs changes to
main - confirm
.internal/is no longer tracked in Git - verify
package.jsonversion is exactly0.1.0-beta.1 - verify GHCR package permissions for
GITHUB_TOKEN - verify the repository has Actions enabled for tag workflows
- verify production and staging env vars are present
- verify a dry-run deployment from the current Dockerfile succeeds
- run
pnpm release:checklocally on a clean tree - review changelog contents for the beta cut
- create and push tag
v0.1.0-beta.1 - watch the GitHub Actions release workflow through image push and GitHub Release creation
- deploy
ghcr.io/contentrain/studio:v0.1.0-beta.1 - run post-deploy smoke checks
If the beta cut fails after tag push, fix forward with v0.1.0-beta.2. Do not mutate or reuse the failed tag.
- Keep
NUXT_SESSION_SECRET_PREVIOUSduring session and BYOA key rotation windows - Treat GHCR image tags as immutable deployment inputs
- Keep release notes aligned with the shipped tag, not with branch head