Add install-mise composite action
CI / validate (push) Successful in 18s
CI / smoke (push) Successful in 2m57s

This commit is contained in:
mdaleo404 committed 2026-10-14 10:47:49 +01:00
1 parent 43b02dfdf1
commit 9cc429e43d
3 files changed
+162 -7

No files matched your search

+5 -1
View File
@@ -31,7 +31,7 @@ jobs:
run: |
set -euo pipefail
status=0
for f in install-grype/action.yml install-syft/action.yml scan-image/action.yml; do
for f in install-grype/action.yml install-syft/action.yml install-mise/action.yml scan-image/action.yml; do
if [ ! -f "$f" ]; then
echo "::error::missing $f"
status=1
@@ -101,11 +101,15 @@ jobs:
- name: Install Syft
uses: ./install-syft
- name: Install mise
uses: ./install-mise
- name: Verify installs
run: |
set -euo pipefail
grype version
syft version
mise --version
- name: Generate SBOM fixture
run: |
+33 -6
View File
@@ -12,6 +12,7 @@ whether a finding notifies, fails the run, blocks a push, or blocks a deploy.
| --- | --- |
| `install-grype` | Installs a pinned, signature-verified Grype (and the pinned Cosign used to verify it) into a job-local directory on `PATH`. |
| `install-syft` | Installs a pinned, signature-verified Syft the same way, for SBOM generation. |
| `install-mise` | Installs a pinned, signature-verified mise (and the pinned minisign used to verify it) into a job-local directory on `PATH`. |
| `scan-image` | Scans an image, directory, or SBOM with Grype and fails the step on fixable findings at the configured severities; writes the raw JSON report. |
## Usage
@@ -124,6 +125,27 @@ Outputs: `syft-path`, `version`.
Supports `amd64` and `arm64`, verified the same way as Grype.
### `install-mise`
| Input | Default | Notes |
| --- | --- | --- |
| `mise-version` | `v2026.10.9` | Release tag including `v`. |
| `minisign-version` | `0.12` | minisign used only to verify mise's checksums. |
| `install-dir` | job-local dir on `PATH` | Override to choose the install location. |
Outputs: `mise-path`, `version`.
Supports `amd64` and `arm64`. This is the one action here that does not use
Cosign, because mise publishes no keyless-signed checksum file: it bootstraps
minisign (itself checksum-pinned, in a tarball carrying both architectures) and
verifies `SHASUMS256.txt` against mise's minisign public key, pinned in the
action, before checking the release archive against the verified file.
Only the `mise` binary is installed. A caller that then runs `mise install`
decides for itself where the toolchain lands (`MISE_DATA_DIR`), and what to do
with the result: `mise` has no audit command, so vulnerability coverage comes
from scanning the installed artifacts.
### `scan-image`
| Input | Default | Notes |
@@ -142,9 +164,11 @@ and `fail-on-fixable` is `true`. Combine with `continue-on-error: true` and
## Versioning
`install-grype` and `install-syft` centralise the Cosign, Grype, and Syft
versions. Bump them here, deliberately, and tag a new release (`v1`, `v2`, ...)
so callers can pin.
`install-grype`, `install-syft` and `install-mise` centralise the Cosign, Grype,
Syft, mise, and minisign versions. Bump them here, deliberately, and tag a new
release (`v1`, `v2`, ...) so callers can pin. The mise action also carries a
minisign bootstrap checksum and mise's minisign public key; bump those together
with the versions they belong to.
## Consuming safely
@@ -154,9 +178,12 @@ release to every consumer:
- **Pin a commit SHA, not a moving ref.** `@v1` can be moved; a full SHA cannot.
- Keep secrets in the calling workflow; nothing here reads or writes secrets.
- Installs verify their payloads against keyless Cosign signatures (and pinned
Cosign checksums) before use, fetching only from
`github.com/sigstore/cosign` and `github.com/anchore/{grype,syft}`.
- Installs verify their payloads before use: `install-grype` and `install-syft`
against keyless Cosign signatures (with checksum-pinned Cosign bootstraps),
and `install-mise` against mise's minisign signature (with a checksum-pinned
minisign bootstrap). They fetch only from `github.com/sigstore/cosign`,
`github.com/anchore/{grype,syft}`, `github.com/jedisct1/minisign` and
`github.com/jdx/mise`.
- Report suspected issues privately to the maintainer (see `CODEOWNERS`) rather
than opening a public issue.
+124
View File
@@ -0,0 +1,124 @@
name: Install mise
description: >-
Install a pinned, signature-verified mise into a job-local directory and add
it to PATH. Also bootstraps the pinned minisign used to verify mise's release
checksums.
# Mechanism only. The caller decides what to install with mise, and what to do
# with the result.
#
# Unlike Grype and Syft, mise publishes no keyless-signed checksum file, so this
# verifies the way mise itself does: minisign over SHASUMS256.txt against a
# pinned public key, then sha256 over the release archive. See README.md.
inputs:
mise-version:
description: mise release tag, including the leading "v".
required: false
default: v2026.10.9
minisign-version:
description: >-
minisign release tag used to verify mise's signed checksums. The release
tarball carries both x86_64 and aarch64 builds.
required: false
default: '0.12'
install-dir:
description: >-
Directory to install the binary into. Defaults to a job-local directory
that is added to PATH.
required: false
default: ''
outputs:
mise-path:
description: Absolute path to the installed mise binary.
value: ${{ steps.install.outputs.mise-path }}
version:
description: Installed mise version string.
value: ${{ steps.install.outputs.version }}
runs:
using: composite
steps:
- id: install
shell: bash
env:
INPUT_MISE_VERSION: ${{ inputs.mise-version }}
INPUT_MINISIGN_VERSION: ${{ inputs.minisign-version }}
INPUT_INSTALL_DIR: ${{ inputs.install-dir }}
run: |
set -euo pipefail
mise_version="${INPUT_MISE_VERSION:?mise-version is required}"
minisign_version="${INPUT_MINISIGN_VERSION:?minisign-version is required}"
install_dir="${INPUT_INSTALL_DIR:-${RUNNER_TEMP:-/tmp}/mise-bin}"
# mise names its assets differently from uname, and minisign names them
# differently from mise. Bump the minisign checksum with its version.
case "$(uname -m)" in
x86_64|amd64)
mise_arch=x64
minisign_arch=x86_64
minisign_sha=9a599b48ba6eb7b1e80f12f36b94ceca7c00b7a5173c95c3efc88d9822957e73
;;
aarch64|arm64)
mise_arch=arm64
minisign_arch=aarch64
minisign_sha=9a599b48ba6eb7b1e80f12f36b94ceca7c00b7a5173c95c3efc88d9822957e73
;;
*)
echo "Unsupported architecture: $(uname -m)" >&2
exit 1
;;
esac
mkdir -p "$install_dir"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
cd "$work"
# Bootstrap minisign: pinned by checksum before it is trusted. One
# tarball serves both architectures.
curl --fail --location --retry 3 --output minisign.tar.gz \
"https://github.com/jedisct1/minisign/releases/download/${minisign_version}/minisign-${minisign_version}-linux.tar.gz"
printf '%s %s\n' "$minisign_sha" minisign.tar.gz | sha256sum --check --status -
tar -xzf minisign.tar.gz "minisign-linux/${minisign_arch}/minisign"
install -m 0755 "minisign-linux/${minisign_arch}/minisign" ./minisign
# mise's minisign public key, copied from minisign.pub in jdx/mise and
# pinned here so it cannot change without a review in this repository.
# Bump deliberately, together with mise-version.
printf '%s\n' \
'untrusted comment: minisign public key 64113EDF160FDEC2' \
'RWTC3g8W3z4RZK3V3qv7fa1QY4JEWyBtqIHW+85QlJpZc5yG+uNYNBSZ' \
> mise.pub
base="https://github.com/jdx/mise/releases/download/${mise_version}"
tarball="mise-${mise_version}-linux-${mise_arch}.tar.gz"
curl --fail --location --retry 3 --output SHASUMS256.txt "${base}/SHASUMS256.txt"
curl --fail --location --retry 3 --output SHASUMS256.txt.minisig "${base}/SHASUMS256.txt.minisig"
# Verify mise's checksum file with the pinned key, then the archive
# against the verified file.
./minisign -V -p mise.pub -m SHASUMS256.txt -x SHASUMS256.txt.minisig
curl --fail --location --retry 3 --output "$tarball" "${base}/${tarball}"
# mise's entries are "./"-prefixed, unlike anchore's bare filenames.
check="$(awk -v t="$tarball" '{ n=$2; sub(/^\.\//, "", n); if (n == t) print }' SHASUMS256.txt)"
[ -n "$check" ] || { echo "No checksum entry for ${tarball}" >&2; exit 1; }
printf '%s\n' "$check" | sha256sum --check --status -
tar -xzf "$tarball"
install -m 0755 mise/bin/mise "$install_dir/mise"
echo "$install_dir" >> "${GITHUB_PATH:-/dev/null}"
version="$("$install_dir/mise" --version 2>/dev/null | awk '{print $1}')"
{
echo "mise-path=$install_dir/mise"
echo "version=${version}"
} >> "${GITHUB_OUTPUT:-/dev/null}"
echo "Installed mise ${version:-$mise_version} at $install_dir/mise"