Resources
Declare resources so consumers can install the completions, man pages, CLI specifications, agent skills, SBOMs, or desktop files that match the tool version a user runs. Resources can live inside an artifact, in a separate release asset, or in the source repository; some can also be generated by the installed executable.
Declare each resource with a --resource flag on packslip create, a
[[resource]] table in a TOML manifest,
or a line in the GitHub action’s resources input.
Consumers choose which resource kinds they support. Declaring a resource describes where to get it; publish separate assets yourself and check archive paths before signing. See Release recipes for complete configurations.
Write a resource entry
A --resource value, like each line of the action’s resources input,
has this form:
KIND[/QUALIFIERS][@SCOPE]=SOURCE:VALUE
KIND says what the file is. The qualifiers, separated by /, narrow
it down, for example to a completion’s shell and executable. SCOPE
limits the entry to one platform; see
Scope resources to the right artifact.
SOURCE is archive, asset, repo, or exec, and VALUE is the
path it reads or the command it runs; see Choose a source.
Repeat --resource for each entry.
For example, completion/zsh=archive:share/zsh/site-functions/_mytool
declares a zsh completion script inside the archive. The equivalent TOML
uses one [[resource]] table, with separate fields for the qualifier and
source:
[[resource]]
kind = "completion"
shell = "zsh"
archive = "share/zsh/site-functions/_mytool"
Repeat the flag or table for each resource. If the archive has a top-level
directory, include it in archive, for example
mytool-1.2.3/share/zsh/site-functions/_mytool.
| Kind | Flag form | TOML fields | Notes |
|---|---|---|---|
completion | completion/SHELL[/BIN], or completion/SHELL,SHELL[/BIN] with exec | shell, or shells with exec; bin | Several shells only with exec, whose command or env contains {shell}. |
man | man[/BIN] | bin | The file’s suffix gives the section, as in mytool.1. |
cli-spec | cli-spec/FORMAT[/BIN] | format, bin | The documented format is usage. |
skill | skill/NAME | name | A directory that holds SKILL.md; as an asset, an archive of that directory. See Agent skills and desktop files. |
sbom | sbom/FORMAT | format | cyclonedx or spdx. Never from exec. |
desktop, icon | desktop, icon | A freedesktop desktop entry, or an icon file. | |
app | app | A macOS .app bundle inside a dmg or zip. archive source only. | |
A kind of your own, such as font | KIND or KIND/NAME | name | Consumers ignore kinds they do not know. |
BIN is the name of one of the release’s executables; see
Completions and CLI specifications
for when an entry must give it. See
Resource kinds for what each kind means to
a consumer.
Choose a source
A resource has a kind and exactly one source. The source decides what pins the content:
| Source | Example flag value | What pins the content |
|---|---|---|
archive | man=archive:share/man/man1/mytool.1 | The containing artifact’s digest. |
asset | sbom/cyclonedx=asset:dist/mytool.cdx.json | The separate file’s digest in the statement. |
repo | skill/mytool=repo:skills/mytool | source.commit, which is required. |
exec | completion/zsh=exec:mytool completion zsh | The executable is verified; its output is not separately signed. |
A repo entry needs the source repository and commit: pass
--source-repo with the repository URL and --commit with the full
commit SHA, or set source.repo and source.commit in TOML. The GitHub
action sets both.
An asset value is a local path: create digests the file and records
only its file name, such as mytool.cdx.json. Upload the file with the
release, and give its download URL with --url mytool.cdx.json=URL (the
file name, without dist/), the entry’s url in TOML, or --url-base
(url_base in TOML). The GitHub action sets --url-base to the
release’s download URL. A file matched both as an artifact, for example
by dist/*, and as an asset is recorded once, as the asset.
create checks that each entry is well formed, digests every asset, and
confirms that an exec command and any bin qualifier name a release
executable. It does not open archives to check archive paths, read the
repository at repo paths, or run exec commands. A mistyped path is
signed as written, and consumers install without that resource. An
archive path starts at the archive’s root, including any top-level
directory such as mytool-1.2.3/. List the archive’s contents
(tar tf) and inspect the result with packslip show before publishing.
Completions and CLI specifications
packslip create ... \
--resource 'completion/bash,zsh,fish=exec:mytool completion {shell}' \
--resource 'cli-spec/usage/mytool=archive:share/usage/mytool.kdl'
The first entry tells a consumer to run mytool completion {shell}, with
{shell} replaced by the requested shell. The second ships a usage spec,
from which consumers generate completions, man pages, and docs. Declare
either entry or both: when both apply, consumers generate completions from
the spec before trying exec.
Completions generated from a usage spec need usage’s completion engine
each time the shell completes.
mise embeds the engine;
other consumers install usage beside the tool. If your users may
install the tool without mise or usage, ship static completion scripts
as well. Consumers use a static script before they generate completions
from the spec.
Every cli-spec names the executable it describes. The flag may leave
the name out (cli-spec/usage=...) only when exactly one executable is
given with --bin or the TOML manifest’s top-level bin. A completion
or man entry must name its executable when the release has more than
one, as in completion/zsh/mytool=archive:share/zsh/site-functions/_mytool
or man/mytool=archive:share/man/man1/mytool.1; create refuses an
entry that leaves it out. In TOML, set bin = "mytool" on the entry.
An exec command runs one of the release’s own executables: its first
word after any NAME=value words must be a bin name, and create
refuses any other program, such as sh. The CLI splits the value on
whitespace and does not interpret shell quoting, pipes, or redirections.
Leading NAME=value words become environment variables, so
--resource 'completion/bash,zsh,fish/mytool=exec:COMPLETE={shell} mytool'
runs mytool with COMPLETE set to each shell. In TOML, exec is an
array, so an argument may contain spaces. This TOML entry is equivalent
to that flag:
[[resource]]
kind = "completion"
bin = "mytool"
shells = ["bash", "zsh", "fish"]
exec = ["mytool"]
env = { COMPLETE = "{shell}" }
Consumers generate exec completions on demand and cache them. Other exec resources require permission to run vendor code during installation. See execution rules.
Man pages and SBOMs
packslip create ... \
--resource 'man=archive:share/man/man1/mytool.1' \
--resource 'sbom/cyclonedx=asset:dist/mytool.cdx.json'
The first entry ships a man page from the archive; the file’s suffix
gives its section. Name the executable (man/mytool=...) when the
release has more than one, as for a completion.
The second ships a CycloneDX SBOM as a separate release file whose
digest the packslip records; use sbom/spdx for SPDX. An SBOM must come
from an archive, asset, or repo source, so that a digest or commit
covers it, and create refuses one from exec. For one SBOM per
platform, declare one entry per platform with an @ scope
(sbom/cyclonedx@linux/x86_64=...), or one per artifact with the TOML
artifact field; see
Scope resources to the right artifact.
Agent skills and desktop files
skill/mytool=repo:skills/mytool points at a directory containing
SKILL.md at source.commit. For example:
[[resource]]
kind = "skill"
name = "mytool"
repo = "skills/mytool"
Set source.repo and the full source.commit in the manifest, or let the
GitHub action set them. To ship a separate archive instead, use
skill/mytool=asset:dist/mytool-skill.tar.gz; put SKILL.md at its root
or under a single top-level directory. Both forms let consumers provide a
skill that matches the installed tool version.
Desktop releases can declare desktop, icon, and app resources.
An app entry points to a macOS application bundle in an artifact; Linux
applications can pair a desktop entry with an icon. See the
desktop recipe
for the layout and artifact scopes, and
resource kinds for the full rules.
Scope resources to the right artifact
An entry without a scope applies to every artifact. When archive layouts
differ by platform, limit the entry to the artifacts of one platform. On
the command line, add the scope to the kind with @:
packslip create ... \
--resource 'man@linux=archive:share/man/man1/mytool.1' \
--resource 'completion/zsh@darwin/aarch64=archive:share/zsh/site-functions/_mytool'
The scope is os, os/arch, or os/arch/libc. It is read from the
kind rather than the value, so an @ inside an exec argv or a path is
left alone. In TOML, set os, arch, or libc on the entry:
[[resource]]
kind = "man"
os = "linux"
archive = "share/man/man1/mytool.1"
Spell scope values as the vocabularies do,
such as darwin and aarch64, not macos or arm64. create accepts
a scope that matches no artifact, and consumers never use such an entry.
Whatever its scope, an archive entry never applies to a bare executable
(format raw, gz, xz, zst, or bz2), which has no paths inside
it. To give the bare build the same man page, add an asset or repo
entry for it.
When archives for the same platform have different layouts, or a
resource belongs to one variant, scope the entry to one exact artifact.
The command line has no exact-artifact scope; use the artifact field
of a TOML [[resource]] table, which names one release file:
[[resource]]
kind = "man"
artifact = "mytool-1.2.3-linux-x64.tar.gz"
archive = "mytool-1.2.3/share/man/man1/mytool.1"
This man page belongs only to the named archive, not to another format or
variant for the same platform. The named artifact must be in the release
and match any platform scope on the entry, or create refuses the entry.
For complete configurations, see Release recipes.
When several entries for the same resource apply, the most specific wins; see How consumers choose among entries.
How consumers choose among entries
Consumers group entries by resource identity before selecting a source. For a completion, the identity is the executable and shell; for a CLI spec, the executable and format; for a skill, its name; for an SBOM, its format. Resource identity gives the identity of every other kind. A bash completion and a zsh completion are separate needs, not fallback choices.
Within one identity, selection proceeds in this order:
- Keep entries that apply to the selected artifact.
- Prefer exact artifact scope, then the most specific platform scope.
- Prefer
archive, thenasset, thenreposources. For a need a static CLI spec can generate, try that before running anexecresource. - Break ties within the same scope and source type by declaration order. Stop once a usable entry satisfies the need.
For example, these equally scoped entries try the current skill directory
first and the older location only if the first is unavailable. The release
must also declare source.repo and source.commit.
[[resource]]
kind = "skill"
name = "mytool"
repo = "skills/mytool"
[[resource]]
kind = "skill"
name = "mytool"
repo = ".agents/skills/mytool"
Fallback cannot rescue a wrongly scoped entry. A more specific entry removes every less specific entry for the same resource before any source is tried, so an unscoped entry never backs up a scoped one whose path is wrong. Scope an archive resource to its artifact when other artifacts do not contain that path.
Every resource is optional: if a consumer cannot fetch any entry for a resource, it reports that and installs the executables without it. A digest or source-commit mismatch fails the installation; it never permits trying a different source. See Fallback and verification and consumer rule 8 for the full rules.