Skip to main content
Version: v5.7.0

3.4 Templates and versions

Included from Basic tiertemplates

After this you can: create a template, pin a version to an exact revision, assign it to assets, and run the version lifecycle with its in-use protections.

Before you start — tick each one:

  • Lesson 3.3 — Git remotes and the identity policy (opschain learn 3.3)

In the GUI: the Templates tab on a project page, beside Git remotes — versions, the assets pinned to each, lock/unlock/archive.

Lesson 1.1 ended with an asset pinned to a template version; this lesson is where that version comes from. A template names which remote its content comes from, a template version pins a revision — branch, tag or commit — of that repository, and every asset pinned to a version takes its action catalog from that exact commit. By the end you will have cut a version, moved assets onto it, and seen the server refuse to pull a version out from under the assets that use it.

1. Create a template and pin its first version​

Both can be made in one command, against the remote from lesson 3.3:

opschain templates create acme \
--code web_tier --name "Web Tier" \
--git-remote infra \
--version 1.0.0 --git-rev v1.0
created template acme/web_tier (01a14002-…-example)
created version 1.0.0 (ready, git rev v1.0 (0a1b2c3))

The server resolves --git-rev against the remote right away: the version stores both the revision you asked for and the exact commit it resolved to, so what a version means never drifts — and a commit you rehearsed locally with --ref before pushing (lesson 2.6) is exactly what pinned assets will run. A revision that does not exist in the repository is a named 422 (Git rev must match a Git revision (branch/tag/commit) in the template's Git remote), not a stored mistake.

Template codes are immutable and lowercase ([a-z0-9_]+). The repository itself carries the template's content — its actions under .opschain/templates/<code>/actions/ — authored exactly as lessons 2.1 and 2.4 taught, which is why a template version is "a revision of a repo" and nothing more.

2. Assign a version to assets​

Assets met templates in lesson 1.1: nodes create … --template web_tier:1.0.0 pins at creation. templates assign is the fleet move — repoint existing assets at a version in one command:

opschain templates assign acme/web_tier --version 1.1.0 \
--asset acme/environments/dev/assets/web \
--asset acme/environments/dev/assets/api
assigned version 1.1.0 of acme/web_tier to 2 asset(s)

Each asset must already belong to this template (the server refuses a cross-template assignment by name), and you need update rights on the template and on every named asset.

How template actions reach the asset catalog. Assigning (or pinning at creation) opens a new entry in the asset's version history and refreshes its action catalog in the background — the server checks out the version's pinned commit and derives the actions it declares. Until the refresh lands, the previous catalog keeps serving, so a broken revision never leaves an asset with no actions. From there it is lesson 3.1's world: opschain actions list shows the catalog, and runs create change executes from it.

opschain nodes show acme/environments/dev/assets/web # template: web_tier:1.1.0
opschain actions list --project acme --env dev --asset web # the refreshed catalog

Declarative YAML action files are the default authoring lane and run on any runner. A template whose repository also carries an actions.rb or a MintModel document — applicable only to existing MintPress customers and those using the MintPress SDK or the packs:mintpress feature — has those actions derived on a Ruby-capable runner and merged into the same catalog, gated by the packs:mintpress entitlement per step; opschain docs packs-mintpress covers that lane in full.

3. The version lifecycle​

Across a whole project, the GUI's Asset versions by environment view draws the matrix: assets as rows, the project's environments as columns, the pinned version in each cell with its locked / floating / archived badges, and every cell that differs from a baseline environment tinted — the one-glance answer to "is prod on the same version as test". It is a view of the project's Assets page only: switch the page from Table to Versions by environment beside the toolbar. The view lists every asset of the project — the project-level assets first, under the environment - with the cell showing the template version each is on, then the environment-scoped assets against every environment, the right-most environment the baseline until you pick another. An environment's Assets page shows the table only. The dashboard panel of the same name shows one project's matrix, project-level assets included, and opens the project's view. On the command line, templates show is the lifecycle dashboard:

opschain templates show acme/web_tier
code: web_tier
id: 01a14002-…-example
project: acme
name: Web Tier
description: -
type: asset
git_remote: infra
archived: false
versions:
VERSION STATE GIT REV LOCKED ARCHIVED IN USE
1.0.0 ready v1.0 (0a1b2c3) no no -
1.1.0 ready main (9f8e7d6) no no 2 asset(s)

New versions come from version upsert — the same command updates an existing version's revision or description:

opschain templates version upsert acme/web_tier --version 1.2.0 --git-rev release/1.2
upserted version 1.2.0 of acme/web_tier (ready, git rev release/1.2 (4c5d6e7))
opschain templates version upsert acme/web_tier --version 1.2.0 --fetch

--fetch re-resolves the stored revision against the remote — a version pinned to a branch picks up its new head; --float makes that the version's standing behaviour: the server fetches every Git remote on its periodic schedule (every 15 minutes unless the remote, the project or the global settings say otherwise — git-remotes show prints the last and next fetch) and repins each floating version whose branch moved, rolling the assets pinned to it onto the new commit and refreshing their action catalogs, with an info:template_version:rev_repinned event on the record. That background refresh runs as the version's creator, so a floating version cannot be owned by a break-glass account: creating or switching a version to floating as one is refused by name — pin the revision, or create the version as an everyday user. The branch a floating version follows is a watched ref: the fetch that moves it also records info:git_remote:ref_moved (the old and new commits, fast_forward or forced), the event a trigger can fire a workflow on — opschain docs triggers ("The git ref-move source") has the recipe, and opschain git-remotes refs acme/origin lists what is watched. Versions that assets are in use by are protected: changing or fetching their revision, unlocking them, or archiving them is refused with a named error —

opschain templates version archive acme/web_tier --version 1.1.0
Error: the server answered HTTP 422: Template version cannot be archived as the version is currently in use

— and --ignore-in-use is the deliberate override: it accepts the risk and, for a revision change, rolls the pinned assets onto the newly resolved commit (their catalogs refresh again).

lock is the release freeze: a locked version accepts no revision or metadata changes until unlocked.

opschain templates version lock acme/web_tier --version 1.1.0
locked version 1.1.0 of acme/web_tier

archive retires a version from assignment without deleting anything; unarchive brings it back. The everyday flow is: upsert the next version → assign it → lock what production runs. The GUI's screens ride the same calls over the v2 API (opschain docs api-v2), with the same in-use refusals.

Check yourself​

  • Archive the version your assets pin: what does the server answer, and which column of templates show tells you why before you try?

What you learned​

  • A template names a remote; a version pins a revision of it and stores the exact commit it resolved to, so a version never drifts.
  • templates assign --version … --asset … repins assets in one move and refreshes their action catalogs in the background, the old catalog serving until the new one lands.
  • version upsert cuts or repins versions, --fetch follows a moving branch once, --float follows it on every periodic fetch of the remote, and lock freezes a release.
  • A version in use refuses a revision change, unlock or archive by name; --ignore-in-use is the explicit override that rolls the assets forward with it.

Next: lesson 3.5 — Secrets and the vault (opschain learn 3.5), or back to your track (opschain learn).