A template source is a Git repository whose *.tensor9.{tf,sh,kubectl}
and *.t9.{tf,sh,kubectl} files become Tensor9 ops command templates
in your control plane. You register the source once; later, as the repo
evolves, you re-sync to discover new templates, upgrade modified ones,
or retire those removed upstream.
This is the canonical way to maintain a library. The open-source
tensor9ine/cmdlib repo is laid out exactly this way and is a good
starting point if you’re new to authoring templates.
Three properties keep authoring sane:
- Templates live in code review. Pull requests against the repo
gate every change before customers ever see it.
- Resync surfaces drift. When the repo lands new templates or
modifies existing ones,
tensor9 ops template source resync shows
the four-bucket diff (unchanged, modified, new, removed) without
committing anything.
- Upgrades are explicit.
tensor9 ops template source upgrade is
a separate step. Diff first, then apply only the picks you want,
scoped by file or action kind.
A reference layout
The cmdlib repo groups templates by category. Each subdirectory
contributes one template per .tensor9.* file inside it:
src/
aws/
find-idle-instances.tensor9.tf
list-public-s3-buckets.tensor9.tf
...
k8s/
drain-node.tensor9.tf
scale-deployment.tensor9.tf
...
linux/
disk-usage.tensor9.tf
host-info.tensor9.tf
...
darwin/
disk-usage.tensor9.tf
host-info.tensor9.tf
...
Both linux/ and darwin/ carry a disk-usage.tensor9.tf. They
coexist because Tensor9 prefixes the parent directory name onto the
imported template, producing linux-disk-usage and darwin-disk-usage
as distinct templates. See “Conflict resolution” below for the cases
where the prefix alone does not disambiguate.
Registering a source
tensor9 ops template source create \
--sourceType GitHub \
--sourceUrl https://github.com/tensor9ine/cmdlib \
--appName my-app \
--sourceName cmdlib
The action shells out to gh to clone the repo at HEAD, walks every
matching file in the tree, ships the raw bytes to the appliance for
parsing, and persists one template per file plus a single
OpsCmdTmplSrc row binding the source to its templates.
Useful flags:
| Flag | Purpose |
|---|
--branch | Override the branch (default: the repo’s default branch). |
--dirs | Comma-separated directories to track (default: every dir containing .tensor9.* files). |
--dry-run | Walk and print what would be imported. No control-plane writes. |
--conflict-policy | How to handle template-name conflicts in the target app. See below. |
gh auth login must succeed first. The action will fail with a clear
message if the GitHub CLI is missing or unauthenticated.
Conflict resolution
The persisted template name is the dir-prefixed file stem
(linux-disk-usage from linux/disk-usage.tensor9.tf). A name
conflict happens when that stem already exists in the target app
because, say, the same source was registered before and you are
re-importing.
--conflict-policy controls the bulk behavior:
| Value | Effect |
|---|
fail (default) | Abort the whole register on the first conflicting name. Safest for first-time imports. |
skip-all | Skip every conflicting file. The non-conflicting subset still imports. |
duplicate-all | Auto-rename each conflicting file with a -N suffix (linux-disk-usage-2, then -3, etc.). |
Example:
# Re-import cmdlib, dropping anything we already track and importing only the new files
tensor9 ops template source create \
--sourceType GitHub \
--sourceUrl https://github.com/tensor9ine/cmdlib \
--appName my-app \
--sourceName cmdlib-v2 \
--conflict-policy skip-all
The action’s output lists the skipped or renamed files explicitly so
you can audit what landed.
Resyncing against HEAD
tensor9 ops template source resync --sourceName cmdlib
Resync clones the repo, lifts each file’s spec, and reports the
four-bucket diff against what’s already imported:
| Bucket | Meaning |
|---|
Unchanged | Tracked template whose source file at HEAD has the same content hash. No action needed. |
Modified | Tracked template whose source file at HEAD has a different content hash. Run tensor9 ops template source upgrade to mint a new lineage version. |
New available | File at HEAD that no tracked template owns. Run tensor9 ops template source upgrade --actions add to import. |
Removed upstream | Tracked template whose source file vanished from HEAD. Run tensor9 ops template source upgrade --actions retire-here to drop the source binding. |
Resync is read-only; it never persists anything. Run it as often as you
like.
Resync walks the whole repo, not just the directories you selected
at register time. If a new sibling directory landed upstream after you
registered (a darwin/ next to your originally-selected linux/), its
files surface as New available automatically. You do not need to
re-register the source to track new dirs.
Applying the diff: upgrade
tensor9 ops template source upgrade --sourceName cmdlib
By default tensor9 ops template source upgrade applies every action
the diff implies: evolve modified templates, add newly-available
ones, retire-here those removed upstream. Tracked template lineages
are versioned, so an upgrade mints v1.1.0 (or the appropriate
semver bump) without breaking customers who pre-approved v1.0.0.
Scoping with --only and --actions
Two filters narrow what the upgrade does:
# Only evolve modified templates; don't add new files or retire missing ones
tensor9 ops template source upgrade \
--sourceName cmdlib \
--actions upgrade
# Only add newly-discovered files; leave existing tracked templates alone
tensor9 ops template source upgrade \
--sourceName cmdlib \
--actions add
# Apply the diff but only for two specific files
tensor9 ops template source upgrade \
--sourceName cmdlib \
--only linux-disk-usage.tensor9.tf,linux-host-info.tensor9.tf
--actions accepts any subset of upgrade, add, retire-here. --only
restricts to a CSV of file names matching what
tensor9 ops template source resync listed.
What “retire-here” means
When a source file vanishes from upstream, customers who ran the
template before still have valid pre-approvals against the persisted
template versions. retire-here deprecates the latest version (so new
runs surface a deprecation notice) and drops the binding from the
source’s tracked id list. The template lineage itself stays in your
control plane until you explicitly revoke or evolve it. Customers can
keep using already-approved versions until pre-approvals expire.
Inventory
# All sources you've registered
tensor9 ops template source list
# A single source's full detail
tensor9 ops template source retrieve --sourceName cmdlib
retrieve shows the GitHub coordinates, last-synced commit, selected
directories, and the imported template id list.
Retiring a source
tensor9 ops template source retire --sourceName cmdlib
Soft-delete. The source row is marked retired; the imported templates
stay. They become orphan lineages: usable, evolveable, and revokable on
their own, but no longer reachable through
tensor9 ops template source resync or
tensor9 ops template source upgrade. Use --auto to skip the
confirmation prompt in scripts.
End-to-end example
# 1. Register the public cmdlib repo as a template source for `my-app`
tensor9 ops template source create \
--sourceType GitHub \
--sourceUrl https://github.com/tensor9ine/cmdlib \
--appName my-app \
--sourceName cmdlib
# 2. Some weeks later, a new `darwin/` dir landed upstream.
# See what's changed:
tensor9 ops template source resync --sourceName cmdlib
# 3. Pull only the new darwin templates (don't touch existing linux ones)
tensor9 ops template source upgrade \
--sourceName cmdlib \
--actions add
# 4. Review what you now expose:
tensor9 ops template list --appName my-app