Upgrading APM Dependencies
Upgrades in APM mean: pointing one or more dependencies at a newer Git tag in calab-ai/apm-registry, regenerating the lockfile, and re-syncing the installed files. This page covers the three commands that drive that loop and how to recover when things go sideways.
The three commands
| Command | What it does | When to run it |
|---|---|---|
gh calab apm update [<id>] | Bumps one (or all) dependencies in apm.yml to the latest GA tag in the catalog, then re-resolves and re-writes the lockfile. | You want newer versions. |
gh calab apm resolve | Reads apm.yml, resolves every tag ref to a commit SHA + file hashes, writes apm.lock.yaml. Does not touch installed files. | You hand-edited apm.yml. |
gh calab apm sync | Reads apm.lock.yaml and writes (or removes) installed files to match. | After update or resolve, or in CI to materialize a checked-in lockfile. |
Mental model:
apm.yml apm.lock.yaml installed files + .generated-from-apm.json
| resolve | sync |
+-----------------> +--------------------------> +
^ |
+----------------------- update ---------------+
(delegates to resolve + sync)Upgrade one dependency
gh calab apm update calab-workspace-baseArgument form:
apm updateaccepts the same forms asapm install— short package name (preferred for packages) or reverse-domain id. Primitives must use the reverse-domain id (e.g.gh calab apm update calab.skill.az-cli).
This:
- Looks up the latest GA tag for that id in the catalog.
- Rewrites the
source.refinapm.yml. - Re-runs the resolver and writes a new
apm.lock.yaml. - Re-runs
apm syncto materialize the new files.
JSON envelope:
{
"ok": true,
"command": "apm update",
"data": {
"updated": [
{ "id": "calab.package.workspace-base", "from": "v1.0.0", "to": "v1.1.0" }
],
"already_latest": []
}
}If everything is already on the latest GA, the id appears in already_latest and exit code is 0.
Upgrade everything
gh calab apm updateNo id → updates every dependency. Output lists every change in data.updated.
Upgrade by hand
When you need to pin to a specific older or pre-release tag:
# 1. Edit apm.yml manually:
# source.ref: refs/tags/packages/calab-workspace-base/v1.2.0-rc.1
#
# 2. Re-resolve and re-sync:
gh calab apm resolve
gh calab apm syncThe resolver rejects branch refs unless policy.allow_branch_refs: true is set in apm.yml (only allowed in dev — never in production manifests).
Lockfile semantics
apm.lock.yaml is the only input to apm sync. The contract is:
- Every dependency in
apm.ymlhas exactly one corresponding lockfile entry. - Every lockfile entry records a resolved commit SHA (never just the tag) and a SHA-256 hash of every file.
- Two consumers running
apm syncagainst the same lockfile produce byte-identical installed trees — that is the whole reason the lockfile exists.
Schema: schemas/apm-lock.schema.json.
Lock-only operations
gh calab apm lock # regenerate apm.lock.yaml in place
gh calab apm lock --check # fail (exit 5) if the lockfile is out of date--check is what you wire into CI to keep the manifest and lockfile in sync.
Resolving lockfile drift
The most common upgrade footgun is a manifest edit without a corresponding lockfile regeneration. Symptoms:
gh calab apm validateexits5(conflict).gh calab apm lock --checkexits5.- CI fails with “lockfile drift detected”.
The fix is always the same:
gh calab apm resolve # regenerate apm.lock.yaml from the current apm.yml
gh calab apm sync # materialize the new files
git add apm.yml apm.lock.yaml .github/copilot .generated-from-apm.json
git commit -m "chore(apm): refresh lockfile"If you intentionally want to roll back a manifest change but keep the existing lockfile, revert the manifest edit instead — never hand-edit the lockfile.
Per-file hash mismatch
gh calab apm validate --verify re-downloads every installed file and recomputes hashes. If a hash mismatches, either:
- a local edit modified an APM-managed file (don’t — APM-managed files are tracked in
.generated-from-apm.jsonand will be safely re-written by the nextsync), or - the upstream tag was force-pushed (file an issue against
calab-ai/apm-registry; tags are immutable by policy).
Recommended CI wiring
- name: APM — verify manifest and lockfile
run: gh calab apm validate
env:
GH_TOKEN: ${{ github.token }}
- name: APM — verify lockfile is current
run: gh calab apm lock --check
env:
GH_TOKEN: ${{ github.token }}
- name: APM — install from lockfile
run: gh calab apm sync
env:
GH_TOKEN: ${{ github.token }}CI must never call gh calab apm install or gh calab apm update — both mutate the manifest. Reserve those for human commits.
Uninstalling
gh calab apm uninstall <id-or-name>Removes the dependency from apm.yml, regenerates the lockfile, and removes only the files previously recorded in .generated-from-apm.json for that id. Any file you added by hand stays put.