GitHub Actions reference

Use this reference to write or review a workflow that runs jdx/packslip or jdx/packslip/releases. It gives each input's default and the release that added it, and how the actions choose and check the packslip CLI. To add the action step by step, follow Publish with GitHub Actions.

Versions and pinning

The actions and the packslip CLI share one version number: each tag vX.Y.Z releases both, even when only an action changed. Both actions take these references; for the release list action, write jdx/packslip/releases in place of jdx/packslip:

ReferenceWhat the workflow runs
jdx/packslip@v1The latest 1.x release; v1 moves to each release's commit once it is published.
jdx/packslip@vX.Y.ZRelease X.Y.Z, which runs CLI X.Y.Z by default.
jdx/packslip@<full-commit-sha> # vX.Y.ZThe same release, fixed to its commit.

A breaking change gets a new major tag, such as v2; after it, workflows on @v1 stop receiving updates until they move to @v2.

GitHub only warns Unexpected input(s) about an input the pinned release does not define, then runs the step without it. Check each input's Since column against the release you pin.

Pin the action to a commit

The action's code decides what your workflow signs. As hardening, pin a release's full commit with the version in a comment, as Publish with GitHub Actions does for actions/upload-artifact:

- uses: jdx/packslip@<full-commit-sha> # vX.Y.Z

Release tags are annotated, so ask for the commit a tag points at:

git ls-remote https://github.com/jdx/packslip 'refs/tags/vX.Y.Z^{}'

Pinning the commit also pins the CLI download on every platform, with no hashes in your workflow; see CLI version and download checks.

Publish action

jdx/packslip signs keylessly. Its job needs these permissions:

It runs these steps:

  1. Installs the packslip CLI; see CLI version and download checks.
  2. Fetches the assets download matches from the release named by tag into a new temporary directory; token must be able to read it.
  3. Collects those and the files artifacts matches, or fails with no artifacts matched.
  4. Attests the collected files, unless attest is link or false; see Build provenance.
  5. Runs packslip create to create and sign the statement.
  6. Verifies the bundle against the workflow's repository and GitHub's issuer.
  7. Uploads the bundle to the release named by tag unless upload is false.

Publish action inputs

InputDefaultSinceDescription
artifacts1.0.0Artifact files or globs already present locally, whitespace separated (dist/*.tar.xz dist/*.zip). At least one file must match between this and download.
download1.0.1Release asset name patterns to fetch before collecting artifacts, whitespace separated (mytool-*.tar.gz mytool-*.zip). Downloaded files join artifacts, so a job whose archives already live on the release named by tag needs no separate gh release download step, and no second copy of these patterns for a local artifacts glob. With contents: read, the default github.token cannot see a draft release.
project1.0.0The project's name; defaults to github.com/<owner>/<repo>. A tool in a monorepo names itself github.com/<owner>/<repo>/<tool> and runs this step once per tool. A host such as mytool.example.com names a project on its own domain; see https://packslip.dev/docs/self-hosting/.
version1.0.0The release's semver version, such as 1.2.3; defaults to the tag without a leading v. Set it for tags such as mytool-v1.2.3 or v4.1, which the action does not normalize.
tag1.0.0The release tag, such as v1.2.3. Defaults to the tag that triggered the workflow and is required otherwise. The action records it as the source tag, derives the default version, url-base, and notes-url from it, and downloads from and uploads to that release.
commit1.3.0Source commit recorded in the release bundle; defaults to github.sha. Set this to the full commit SHA of the release tag when publishing a different revision, for example from a manually dispatched workflow. This does not change the workflow identity or artifact provenance.
url-base1.0.0Download URL prefix; defaults to this release's download URL
bin1.0.0Executables inside every artifact, whitespace separated: a path relative to the archive root, a plain name to look up inside each archive (tool finds tool-1.2.3-linux-x64/tool), or NAME=PATH when the command name differs from the file's. For a bare executable artifact, the value is the name the file gets on PATH.
manifest1.0.0A release.toml file (packslip create --manifest) for what the inputs cannot say per artifact: executables at different paths in different archives, a platform the file name does not give, an artifact that runs anywhere, OS or glibc minimums, and resources for one artifact. Artifacts it lists are merged with the collected files. The action always sets project, version, url_base, notes_url, and [source] itself, from its inputs, their defaults, and the workflow's repository, so those keys in the file are ignored. The file must be present in the job, so check out the repository first.
variants1.0.0Variants for artifacts that share a platform, as FILENAME=VARIANT, whitespace separated (tool-fips-linux-x64.tar.gz=fips).
formats1.0.0The format of an artifact whose name does not say, as FILENAME=FORMAT, whitespace separated (tool-linux-x64.bin=raw). Every artifact needs one; the name gives it for the usual archive, installer, and bare executable spellings, and this is for the rest.
resources1.0.0What ships besides the executables, one per line, each a packslip create --resource value: KIND[/QUALIFIER][@os[/arch[/libc]]]=SOURCE:VALUE. For example: completion/zsh=archive:share/zsh/site-functions/_tool, man=archive:share/man/man1/tool.1, cli-spec/usage=exec:tool usage, skill/tool=repo:skills/tool, skill/tool=asset:dist/tool-skill.tar.gz, or sbom/cyclonedx=asset:dist/tool.cdx.json. An @ scope limits an entry to one platform (man@linux=archive:share/man/man1/tool.1). An asset is a local file that is digested with the artifacts; upload it to the release yourself, because the action uploads only the bundle. A file the artifacts input also matched is recorded as the asset, not as an artifact.
require1.0.0Commands the executables need on PATH, one per line, each a packslip create --require value: bin:NAME or bin:NAME@MIN, such as bin:java@17. What the executables load from the host is read from them and needs no input.
notes-url1.0.0URL of the release notes; defaults to the release page
extensions1.0.0Release-level extensions, one per line, each a packslip create --extension value: NAME=JSON, where NAME is who defines the value (a consumer such as mise, or a domain the vendor controls) and JSON is the value, such as example.com={"build_id":"20260901.3"}.
attesttrue1.0.0What to do about build provenance. true attests every file collected by artifacts and download with actions/attest-build-provenance, links each artifact's attestation from the packslip, and needs the attestations: write permission; an artifact listed only in the manifest input's release.toml is neither attested nor linked. link only links the attestations, for a workflow whose build jobs already attested the files as they produced them; it needs no extra permission, and an artifact nothing attested leaves a URL that resolves to nothing. false does neither. In a private or internal repository, GitHub attestations need GitHub Enterprise Cloud, so use false on other plans.
outpackslip1.0.0Directory to write the bundle into. The file is packslip.sigstore.json, or packslip.<subpath>.sigstore.json for a tool in a GitHub monorepo (github.com/<owner>/<repo>/<subpath>), with any / in the subpath written as -.
uploadtrue1.0.0Upload the bundle to the release named by tag, which needs contents: write. Defaults to true. Set false to sign in a job that has only contents: read, then upload the bundle output from a separate job that has contents: write; see https://packslip.dev/docs/publishing/#keep-the-action-away-from-release-write-access.
packslip-version1.0.0The packslip version to run, such as 1.4.0, without a leading v; defaults to the version this action was released with (read from its Cargo.toml). An explicit override bypasses the internal release digest map with a warning; use packslip-sha256 for an explicit archive pin. Ignored when packslip-path is set.
packslip-sha2561.6.0SHA-256 digest of the packslip release archive the action downloads. The default version is internally pinned. A supplied digest must agree with that map, or pin an explicit packslip-version override. When set, the action checks the downloaded archive before verifying its build provenance or extracting it. It is ignored when packslip-path is set, because the workflow then supplies the executable itself.
packslip-path1.0.0An existing packslip executable to run instead of downloading a release: a path, or a name to look up on PATH. For a platform with no release asset the job can run, build the CLI first (cargo install packslip --locked --root "$RUNNER_TEMP/packslip") and point this at it. The action runs the binary as given and does not check its provenance, so the job is responsible for where it came from.
token${{ github.token }}1.0.0Token for installing packslip, downloading release assets (download), and uploading the bundle (upload)

Publish action outputs

OutputSinceDescription
bundle1.0.0Path of the written bundle, under out

How inputs reach packslip create

The action always passes --project, --version, --tag, --commit, --url-base, --notes-url, and --source-repo to packslip create, from its inputs or their defaults. They override a release.toml given as manifest. --source-repo is always the workflow's repository, https://github.com/<owner>/<repo>. commit sets only source.commit in the statement, not the signing identity or the build provenance.

Besides those seven flags, the inputs map this way:

InputWhat the action passes to packslip create
artifacts, download, variantsEach collected file as a positional ARTIFACTS argument: PATH, or PATH@VARIANT when a variants entry names it
bin, formats--bin, --format for each entry
resources, require, extensions--resource, --require, --extension for each line
manifest, out--manifest, --out
attest set to true or link--provenance NAME=URL for each collected file

upload, token, and the packslip- inputs have no flag. A project that runs packslip create in place of the action, such as one that needs --no-pin-workflow, passes the same flags; packslip create documents each one.

Build provenance

attest: true, the default, attests the collected files and links each attestation from the packslip. link only links, and false does neither; use false in a private or internal repository without GitHub Enterprise Cloud. Each link is https://api.github.com/repos/<owner>/<repo>/attestations/sha256:<digest>.

Before you switch to false after releases that carried provenance, read Keep later releases acceptable.

Release list action

jdx/packslip/releases turns the bundles a project has published, laid out as <dir>/<tag>/packslip.sigstore.json, into a signed release list. See Host releases on your own domain for the workflow and Manage release lists for what a list does. It signs keylessly, so its job needs id-token: write. It runs these steps:

  1. Installs the packslip CLI as the publish action does.
  2. Verifies every bundle under one identity policy (issuer with identity or identity-prefix), refusing one whose source tag differs from its directory's name.
  3. Builds and signs the list with packslip releases, which refuses a bundle for another project. A tag in yank or security stands for <url-base>/<tag>/<bundle>; a URL is used as given.
  4. Verifies the written list under the same identity policy.

With no bundles in dir, the action fails with no packslip.sigstore.json under <dir>/<tag>/; the action does not build an empty list. To publish a list with no releases, follow Retire every entry.

Release list action inputs

InputDefaultSinceDescription
projectRequired1.1.0The project's name, as its bundles spell it: a host such as mytool.example.com, or a host with a path.
dirRequired1.1.0A directory holding the published bundles as <dir>/<tag>/<bundle>, one tag directory per release, as synced down from the download host.
url-baseRequired1.1.0Where the bundles are served, without the tag: https://<host>. Each entry's URL is <url-base>/<tag>/<bundle>.
bundlepackslip.sigstore.json1.1.0The bundle file name inside every tag directory. packslip create names every domain project's bundle packslip.sigstore.json, including a project with a path such as example.com/tool, so set this only if you uploaded the bundles under another name.
sequence1.1.0The list's sequence, an integer that must increase with every list published; defaults to the current Unix time, which does so with no state to keep. A consumer refuses a list whose sequence is below one it accepted, so a project that switches to the default cannot go back to small hand-picked numbers.
valid-for30d1.1.0How long the list stays current, as a number and unit (30d, 12h, 2w). Publish a fresh list before it expires, whether or not a release shipped.
latest1.1.0Recommend this exact listed version for unconstrained latest requests. Give the version, such as 1.2.3, not the tag that yank and security take. Empty leaves consumers to take GitHub's latest release for a GitHub project, and the highest eligible version for any other.
yank1.1.0Releases to mark withdrawn, one per line, as TAG=REASON or URL=REASON. A withdrawn release stays in the list with its status. These marks apply to this run's list only; a later run without them drops them.
security1.1.0Releases that fix a vulnerability, one per line, as a TAG or URL. These marks apply to this run's list only; a later run without them drops them.
identity-prefix1.1.0The certificate identity prefix every input bundle, and the output list, must verify under; defaults to this repository's workflows.
identity1.1.0A certificate identity, ref included, that every input bundle and the output list must have, instead of identity-prefix; a GitHub or GitLab repository name in it matches in any case. GitHub identities name the ref the workflow ran on (.../release.yml@refs/tags/v1.2.3), so bundles signed on different tags never share one. This suits only bundles and a list all signed from one workflow at one ref; most projects want identity-prefix.
issuerhttps://token.actions.githubusercontent.com1.1.0The OIDC issuer of that identity
allow-other-repositoryfalse1.11.0Set to true to accept bundles that match identity-prefix or identity but were signed from a repository other than the project's, such as an owner's shared build repository. Otherwise packslip verify also requires a github.com or gitlab.com project's bundles to be signed from the repository its name gives. The action lifts that on its own when project names a repository other than the one running it, as a registry's or repackager's list does, so set this only for the project's own repository.
outpackslip-releases.sigstore.json1.1.0Where to write the list. Publish it at the project's well-known URL: https://<host>/.well-known/packslip.json, or https://<host>/.well-known/packslip/<path>.json for a project with a path.
packslip-version1.1.0The packslip version to run, such as 1.4.0, without a leading v; defaults to the version this action was released with (read from its Cargo.toml). An explicit override bypasses the internal release digest map with a warning; use packslip-sha256 for an explicit archive pin. Ignored when packslip-path is set.
packslip-sha2561.6.0SHA-256 digest of the packslip release archive this action downloads. The default version is internally pinned. A supplied digest must agree with that map, or pin an explicit packslip-version override. It is checked before provenance verification and extraction, and is ignored when packslip-path supplies the executable.
packslip-path1.1.0An existing packslip executable to run instead of downloading a release: a path, or a name to look up on PATH. The action runs the binary as given and does not check its provenance.
token${{ github.token }}1.1.0Token for downloading packslip

Release list action outputs

OutputSinceDescription
list1.1.0Path of the written list
count1.1.0How many releases the list names

CLI version and download checks

By default, an action runs the CLI release with its own version, read from its commit's Cargo.toml. packslip-version picks another release. The action downloads packslip-vX.Y.Z-<os>-<arch>.tar.xz, or .zip on Windows, for linux, darwin, or windows on x64 or arm64. There is no darwin-x64 archive; see Build the CLI on the runner.

Each release's action commit includes a digest map: the SHA-256 of the CLI archive for every platform packslip ships. With the default version, the action checks the downloaded archive against that map. It then verifies the archive's build provenance from jdx/packslip with gh attestation verify, and only then extracts and runs it. A packslip-sha256 set alongside the default version must agree with the map. An action commit without the map stops with the action checkout has no release digest map; use a published action commit unless packslip-version or packslip-path is set.

packslip-path runs an executable the job already has (a path, or a name on PATH) as packslip for the remaining steps. It downloads nothing and skips both checks, so the job vouches for the binary. It takes precedence over packslip-version and packslip-sha256, and the action warns when either is also set.

Override the CLI version

With packslip-version set, the action skips the digest map, with a warning, and still verifies the archive's build provenance from jdx/packslip. To pin that archive too, set packslip-sha256:

- uses: jdx/packslip@<full-commit-sha> # vX.Y.Z
  with:
    packslip-version: 1.10.1
    packslip-sha256: <64 lowercase hex digits>
    artifacts: dist/*.tar.xz
    bin: mytool

Take the digest from the asset's metadata on GitHub:

gh release view v1.10.1 --repo jdx/packslip --json assets \
  --jq '.assets[] | select(.name == "packslip-v1.10.1-linux-x64.tar.xz") | .digest | ltrimstr("sha256:")'

Each platform's archive has its own digest, so a matrix that spans platforms gives each runner its own.

Runner requirements

GitHub-hosted Linux and Windows runners have every tool the actions use. Check macOS and self-hosted runners for these:

ToolUsed for
bash 4.4 or laterEvery step; macOS's own /bin/bash 3.2 is too old.
GitHub CLI (gh) 2.68.0 or laterInstalling and verifying packslip, downloading assets, and uploading the bundle. An older gh fails the default CLI's provenance check.
tar with xz support, or unzip on WindowsUnpacking the CLI.
sha256sumLinking provenance by digest and checking the CLI download; shasum also works for the download.
jqReading the digest map, and in the releases action each bundle's tag.