mirror of
https://github.com/jdx/mise-action.git
synced 2026-05-14 05:50:31 +00:00
feat: add wings_enabled input (mise-wings cache integration)
Adds a single new input — `wings_enabled` — that gates the [mise-wings](https://mise-wings.en.dev) asset cache for tool installs. Existing workflows are unaffected: default `false` is a no-op. | Input | Default | Description | |---|---|---| | `wings_enabled` | `false` | Route tool-install URLs through the wings cache when `true` | ## How it works When `wings_enabled: true`, the action exports `MISE_WINGS_ENABLED=1`. Authentication is fully automatic — mise itself owns the GHA OIDC → wings session exchange. No `mise wings login` step in workflow YAML, no long-lived secrets to rotate. When mise (built with wings support — see jdx/mise#9458) sees `MISE_WINGS_ENABLED=1` and detects the GHA OIDC env vars (`ACTIONS_ID_TOKEN_REQUEST_URL` + `ACTIONS_ID_TOKEN_REQUEST_TOKEN`), it: 1. Fetches the runner's OIDC token, scoped to the wings deployment audience 2. POSTs it to `https://api.<host>/auth` to mint a wings CI session JWT 3. Caches the JWT in-process for the rest of the workflow 4. Transparently rewrites `registry.npmjs.org` / `github.com` / `api.github.com` URLs to the wings cache subdomains and attaches the JWT as a Bearer header ## Why opt-in (not opt-out) The default-off posture is deliberate. Many workflows already declare `permissions: id-token: write` for unrelated reasons (SLSA provenance, AWS OIDC, Sigstore, npm provenance). If `wings_enabled` defaulted to `true`, those workflows would silently send the runner's OIDC identity claims to a third-party cache without explicit consent. Cursor Bugbot HIGH + Greptile P1+security flagged a prior "default true" iteration of this PR as a privacy regression. Explicit opt-in keeps the gate visible in the workflow YAML. ## Workflow requirements ```yaml permissions: id-token: write # required for OIDC jobs: build: steps: - uses: jdx/mise-action@<sha> with: wings_enabled: true ``` The action emits a clear warning when `wings_enabled: true` but `id-token: write` is missing — without that hint, the user would see "wings configured but doing nothing" and have no clue why. ## Notes - Older mise binaries see `MISE_WINGS_ENABLED` and silently ignore it — forward-compatible. - `setupMise` fetches the mise binary itself with `curl`, which doesn't go through mise's HTTP layer; the wings rewriter only kicks in once the resulting mise binary runs `mise install`. The action sets the env var before any `mise` subcommand runs.
This commit is contained in:
parent
0a780158e1
commit
969042fe52
4 changed files with 148 additions and 1 deletions
30
action.yml
30
action.yml
|
|
@ -85,6 +85,36 @@ inputs:
|
|||
description: "Automatically load mise env vars into GITHUB_ENV. Note that PATH modifications are not part of this."
|
||||
required: false
|
||||
default: "true"
|
||||
wings_enabled:
|
||||
description: |
|
||||
[experimental] Opt in to the mise-wings asset cache
|
||||
(https://mise-wings.en.dev) for this action invocation.
|
||||
|
||||
When `true`, the action exports `MISE_WINGS_ENABLED=1` so
|
||||
the installed mise binary routes tool-install URLs (npm
|
||||
tarballs, GitHub release artifacts) through the per-org
|
||||
wings cache subdomains.
|
||||
|
||||
Authentication is automatic via the runner's GitHub OIDC
|
||||
identity — no `mise wings login` step, no long-lived
|
||||
secret to rotate. The workflow must declare
|
||||
`permissions: id-token: write` so the OIDC token-issuer
|
||||
env vars are populated; without that, mise falls through
|
||||
to direct-origin fetches transparently.
|
||||
|
||||
Default `false` is the conservative posture: a workflow
|
||||
with `id-token: write` (used for SLSA / AWS-OIDC /
|
||||
Sigstore / etc.) should not have its OIDC token sent to
|
||||
a third-party cache without explicit opt-in. Older mise
|
||||
binaries that don't speak wings ignore the env var
|
||||
entirely, so this is forward-compatible.
|
||||
|
||||
Requires an active mise-wings subscription on the Clerk
|
||||
org linked to the GitHub org running the workflow;
|
||||
without one, the proxy 402s and mise leaves the cache
|
||||
off without affecting the workflow's success.
|
||||
required: false
|
||||
default: "false"
|
||||
outputs:
|
||||
cache-hit:
|
||||
description: A boolean value to indicate if a cache was hit.
|
||||
|
|
|
|||
55
dist/index.js
generated
vendored
55
dist/index.js
generated
vendored
|
|
@ -85677,6 +85677,24 @@ async function run() {
|
|||
else {
|
||||
setOutput('cache-hit', false);
|
||||
}
|
||||
// Wings opt-in hook (experimental). When
|
||||
// `wings_enabled: true` is set, this exports
|
||||
// `MISE_WINGS_ENABLED=1` so subsequent `mise install`
|
||||
// commands in this workflow route through the wings
|
||||
// cache. Default `false` so workflows with
|
||||
// `id-token: write` (used for SLSA / AWS-OIDC / Sigstore /
|
||||
// etc.) don't silently send the runner's OIDC token to
|
||||
// a third-party cache without explicit consent.
|
||||
//
|
||||
// Note: `setupMise` fetches the mise binary itself with
|
||||
// `curl`, which doesn't go through mise's HTTP layer —
|
||||
// the wings rewriter only kicks in once the resulting
|
||||
// mise binary runs `mise install` and friends. Ordering
|
||||
// here is irrelevant for binary acceleration; we just
|
||||
// want the env var set before any `mise` subcommand
|
||||
// runs. Greptile + Gemini both flagged the previous
|
||||
// comment as overstating what the early call accelerates.
|
||||
setupWings();
|
||||
const version = getInput('version');
|
||||
const fetchFromGitHub = getBooleanInput('fetch_from_github');
|
||||
await setupMise(version, fetchFromGitHub);
|
||||
|
|
@ -85704,6 +85722,43 @@ async function run() {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Opt in to mise-wings caching for this workflow run. When
|
||||
* `wings_enabled: true`, exports `MISE_WINGS_ENABLED=1` so
|
||||
* subsequent `mise install` commands route through the
|
||||
* cache.
|
||||
*
|
||||
* Mise itself owns the OIDC → wings session exchange — when
|
||||
* it sees `MISE_WINGS_ENABLED=1` and the GHA OIDC env vars
|
||||
* (`ACTIONS_ID_TOKEN_REQUEST_URL` +
|
||||
* `ACTIONS_ID_TOKEN_REQUEST_TOKEN`), it fetches the runner's
|
||||
* OIDC token, exchanges it at the proxy's `POST /auth`
|
||||
* route, and caches the resulting session JWT for the rest
|
||||
* of the process.
|
||||
*
|
||||
* Pre-flight check: `id-token: write` permission must be
|
||||
* declared at the workflow or job level for the OIDC env
|
||||
* vars to be present. We log a warning when wings is
|
||||
* enabled but the env vars are absent — without this hint,
|
||||
* the user sees a transparent "wings configured but doing
|
||||
* nothing" which is hard to debug.
|
||||
*/
|
||||
function setupWings() {
|
||||
if (!getBooleanInput('wings_enabled')) {
|
||||
return;
|
||||
}
|
||||
exportVariable('MISE_WINGS_ENABLED', '1');
|
||||
info("mise-wings: enabled. mise will exchange the runner's OIDC token for a wings session on first use.");
|
||||
const oidcUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
||||
const oidcToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
||||
if (!oidcUrl || !oidcToken) {
|
||||
warning('mise-wings: GHA OIDC env vars are missing. Add ' +
|
||||
'`permissions: id-token: write` at the workflow or job ' +
|
||||
'level so the runner can mint OIDC tokens. Without this, ' +
|
||||
'mise falls through to direct-origin fetches and the cache ' +
|
||||
'is bypassed.');
|
||||
}
|
||||
}
|
||||
async function exportMiseEnv() {
|
||||
startGroup('Exporting mise environment variables');
|
||||
const cwd = getCwd();
|
||||
|
|
|
|||
2
dist/index.js.map
generated
vendored
2
dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
62
src/index.ts
62
src/index.ts
|
|
@ -54,6 +54,25 @@ async function run(): Promise<void> {
|
|||
core.setOutput('cache-hit', false)
|
||||
}
|
||||
|
||||
// Wings opt-in hook (experimental). When
|
||||
// `wings_enabled: true` is set, this exports
|
||||
// `MISE_WINGS_ENABLED=1` so subsequent `mise install`
|
||||
// commands in this workflow route through the wings
|
||||
// cache. Default `false` so workflows with
|
||||
// `id-token: write` (used for SLSA / AWS-OIDC / Sigstore /
|
||||
// etc.) don't silently send the runner's OIDC token to
|
||||
// a third-party cache without explicit consent.
|
||||
//
|
||||
// Note: `setupMise` fetches the mise binary itself with
|
||||
// `curl`, which doesn't go through mise's HTTP layer —
|
||||
// the wings rewriter only kicks in once the resulting
|
||||
// mise binary runs `mise install` and friends. Ordering
|
||||
// here is irrelevant for binary acceleration; we just
|
||||
// want the env var set before any `mise` subcommand
|
||||
// runs. Greptile + Gemini both flagged the previous
|
||||
// comment as overstating what the early call accelerates.
|
||||
setupWings()
|
||||
|
||||
const version = core.getInput('version')
|
||||
const fetchFromGitHub = core.getBooleanInput('fetch_from_github')
|
||||
await setupMise(version, fetchFromGitHub)
|
||||
|
|
@ -79,6 +98,49 @@ async function run(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opt in to mise-wings caching for this workflow run. When
|
||||
* `wings_enabled: true`, exports `MISE_WINGS_ENABLED=1` so
|
||||
* subsequent `mise install` commands route through the
|
||||
* cache.
|
||||
*
|
||||
* Mise itself owns the OIDC → wings session exchange — when
|
||||
* it sees `MISE_WINGS_ENABLED=1` and the GHA OIDC env vars
|
||||
* (`ACTIONS_ID_TOKEN_REQUEST_URL` +
|
||||
* `ACTIONS_ID_TOKEN_REQUEST_TOKEN`), it fetches the runner's
|
||||
* OIDC token, exchanges it at the proxy's `POST /auth`
|
||||
* route, and caches the resulting session JWT for the rest
|
||||
* of the process.
|
||||
*
|
||||
* Pre-flight check: `id-token: write` permission must be
|
||||
* declared at the workflow or job level for the OIDC env
|
||||
* vars to be present. We log a warning when wings is
|
||||
* enabled but the env vars are absent — without this hint,
|
||||
* the user sees a transparent "wings configured but doing
|
||||
* nothing" which is hard to debug.
|
||||
*/
|
||||
function setupWings(): void {
|
||||
if (!core.getBooleanInput('wings_enabled')) {
|
||||
return
|
||||
}
|
||||
core.exportVariable('MISE_WINGS_ENABLED', '1')
|
||||
core.info(
|
||||
"mise-wings: enabled. mise will exchange the runner's OIDC token for a wings session on first use."
|
||||
)
|
||||
|
||||
const oidcUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL
|
||||
const oidcToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
|
||||
if (!oidcUrl || !oidcToken) {
|
||||
core.warning(
|
||||
'mise-wings: GHA OIDC env vars are missing. Add ' +
|
||||
'`permissions: id-token: write` at the workflow or job ' +
|
||||
'level so the runner can mint OIDC tokens. Without this, ' +
|
||||
'mise falls through to direct-origin fetches and the cache ' +
|
||||
'is bypassed.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function exportMiseEnv(): Promise<void> {
|
||||
core.startGroup('Exporting mise environment variables')
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue