Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,18 @@ jobs:

# ──────────────────────────────────────────────────────────────────────
# Real-world smoke: pull @anthropic-ai/claude-code from npm, build a
# native binary per runner OS/arch with Zstd-compressed pkg payload +
# tar.gz archive, execute the binary with --version, then verify the
# archive round-trips and the sha256 sidecar matches the archive bytes.
# native binary per runner OS/arch in SEA mode with Zstd-compressed
# bundled sources, tar.gz archive, execute the binary with --version,
# then verify the archive round-trips and the sha256 sidecar matches
# the archive bytes.
#
# claude-code is ESM, so mode: sea (standard pkg can't bytecode-compile
# ESM). Each matrix entry builds for the runner's native target so the
# produced binary can be launched in-place for the smoke assertion,
# giving real cross-OS + cross-arch coverage (x64 and arm64 on Linux,
# arm64 on macOS, x64 on Windows). Zstd bundled-binary compression
# requires Node.js >= 22.15 on the build host; .node-version is 22.22.
# claude-code is ESM, so SEA mode is required (standard pkg can't
# bytecode-compile ESM). Both `sea: true` and `compress: 'Zstd'` are
# declared in the pkg config (package.json#pkg) — expressible in
# config starting @yao-pkg/pkg v6.19.0 (yao-pkg/pkg#263). Each matrix
# entry builds for the runner's native target so the produced binary
# can be launched in-place, giving real cross-OS + cross-arch
# coverage (x64 and arm64 on Linux, arm64 on macOS, x64 on Windows).
claude-code-smoke:
name: claude-code-smoke / ${{ matrix.os }} / ${{ matrix.target }}
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -332,8 +334,13 @@ jobs:
const p = './package.json';
const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
pkg.bin = './' + process.argv[1];
// Full pkg-layer config lives here — no CLI-mirror inputs.
// Requires @yao-pkg/pkg >= 6.19.0 for \`compress\` in config
// (yao-pkg/pkg#263).
pkg.pkg = {
assets: ['node_modules/@anthropic-ai/claude-code/**/*']
assets: ['node_modules/@anthropic-ai/claude-code/**/*'],
sea: true,
compress: 'Zstd',
};
fs.writeFileSync(p, JSON.stringify(pkg, null, 2));
" "$entry"
Expand All @@ -350,11 +357,10 @@ jobs:
with:
config: ${{ steps.fix.outputs.fixture }}/package.json
targets: ${{ matrix.target }}
mode: sea
compress-node: Zstd
# Zstd landed in @yao-pkg/pkg 6.17. The action's default
# (~6.16.0) predates it, so pin a Zstd-capable version.
pkg-version: ~6.18.0
# 6.19.0+ is required for the `compress` key in pkg config.
# SEA (`sea: true`) and Zstd (`compress: 'Zstd'`) live in the
# package.json#pkg field, not as action inputs.
pkg-version: ~6.19.0
compress: tar.gz
checksum: sha256
filename: 'claude-{version}-{os}-{arch}'
Expand Down
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,63 @@ Tracking issue: [yao-pkg/pkg#248](https://github.com/yao-pkg/pkg/issues/248).
path: "${{ join(fromJson(steps.build.outputs.artifacts), '\n') }}"
```

## pkg configuration

The action does **not** mirror pkg's CLI flags as inputs. Pkg-specific knobs
— SEA mode, bundled Node compression, `public` / `publicPackages`, V8
`options`, `noBytecode`, `noDict`, `debug`, bytecode-fabricator fallback —
live in your pkg config file (`.pkgrc.json`, `pkg.config.{js,ts,json}`, or
the `pkg` field of `package.json`). See
[yao-pkg/pkg's README](https://github.com/yao-pkg/pkg#config) for the full
schema.

Example — SEA mode with Brotli-compressed bundle + fallback, saved as `.pkgrc.json`:

```json
{
"bin": "src/main.js",
"sea": true,
"compress": "Brotli",
"fallbackToSource": true
}
```

```yaml
- uses: yao-pkg/pkg-action@v1
with:
config: .pkgrc.json
targets: node22-linux-x64
```

### Inline config

For trivial setups you can skip the file and pass the config as a JSON string
via `config-inline` — the action writes it to a temp file and points pkg at
it. Mutually exclusive with `config`.

```yaml
- uses: yao-pkg/pkg-action@v1
with:
targets: node22-linux-x64
config-inline: |
{
"bin": "src/main.js",
"sea": true,
"compress": "Brotli"
}
```

> The value is registered with `core.setSecret`, so exact matches are redacted
> from logs — but it is still written verbatim to a temp file on the runner.
> Prefer `config` (a committed file) for anything beyond a couple of knobs,
> and source long-lived secrets from GitHub Actions secrets / OIDC, not from
> `config-inline`.

The action's inputs cover only concerns pkg config cannot express: matrix
targets, pkg install version/path, archive format, filename template,
checksum algorithms, Windows-metadata resedit patch, macOS/Windows signing,
cache, step summary.

## Outputs

| Output | Shape |
Expand Down
42 changes: 37 additions & 5 deletions STATUS.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
meta:
repo: yao-pkg/pkg-action
branch: main
last-updated: '2026-04-23'
last-updated: '2026-04-24'
action-status: ALPHA — ready for early adopters to try in their own workflows
ci-status: green (222 unit tests)
ci-status: green (227 unit tests)
e2e-status: |
6 jobs after the scope cut — tiny-cjs (ubuntu/macos/windows), codegen-drift,
matrix plan→fanout, multi-target-linux, windows-metadata. Signing e2e still
deferred until live credentials are available.
7 jobs after the scope cut — tiny-cjs (ubuntu/macos/windows), codegen-drift,
matrix plan→fanout, multi-target-linux, windows-metadata, claude-code-smoke
(SEA + Zstd, 4 OS/arch combos). Signing e2e still deferred until live
credentials are available.

# ─── Scope decision (2026-04-23) ─────────────────────────────────────────
#
Expand Down Expand Up @@ -48,6 +49,37 @@ recent-hardening:
- '`yamlString` codegen helper throws on embedded control chars — prevents silent action.yml corruption (S2).'
- 'CI coverage gate at 85% via scripts/check-coverage.ts parsing lcov (S6).'

# ─── Input-surface slim (2026-04-23) ─────────────────────────────────────
# Stop mirroring pkg's CLI. Pkg-specific knobs live in the user's pkg config;
# the action owns only concerns pkg config cannot express (CI-matrix targets,
# pkg install version, archive, checksum, windows-metadata, signing, cache).

input-surface-slim:
dropped-inputs:
- mode
- node-version
- compress-node
- fallback-to-source
- public
- public-packages
- options
- no-bytecode
- no-dict
- debug
- extra-args
migration: 'Move each value into .pkgrc / pkg.config.{js,ts,json} or the `pkg` field of package.json. `buildPkgArgs` forwards the config path to pkg.'
breaking: 'yes — users setting any dropped input will see the unknown-input warning and the value will be ignored. Release note required.'
minimum-pkg-version: |
@yao-pkg/pkg >= 6.19.0 is required for the full build-flag surface in
pkg config. The default `pkg-version` input is pinned to `~6.19.0`.
upstream-dependency: |
RESOLVED in @yao-pkg/pkg v6.19.0 (2026-04-24) via yao-pkg/pkg#263 —
closes yao-pkg/pkg#262. All CLI-only build flags now have config
equivalents: compress, fallbackToSource, public, publicPackages,
options, bytecode (inverts --no-bytecode), nativeBuild (inverts
--no-native-build), noDictionary (renamed from --no-dict), debug,
signature.

# ─── Removed (2026-04-23) ────────────────────────────────────────────────
# Distribution scope reset. Use dedicated downstream actions instead.

Expand Down
51 changes: 7 additions & 44 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,16 @@ branding:

inputs:
config:
description: 'Path to a pkg config (.pkgrc, pkg.config.{js,ts,json}, or package.json). Auto-detected when omitted.'
description: 'Path to a pkg config (.pkgrc, pkg.config.{js,ts,json}, or package.json). Auto-detected when omitted. Mutually exclusive with config-inline.'
config-inline:
description: 'Pkg config as a JSON string. Written to a temp file and passed to pkg via --config. Mutually exclusive with config. Registered with core.setSecret so exact matches are redacted from logs; still written to a temp file on the runner, so prefer config for anything beyond trivial knobs.'
entry:
description: 'Entry script when not specified in the config.'
targets:
description: 'Comma- or newline-separated pkg target triples, e.g. node22-linux-x64,node22-macos-arm64. Defaults to the host target.'
mode:
description: 'standard | sea — selects pkg Standard or SEA mode.'
default: 'standard'
node-version:
description: 'pkg''s bundled Node.js major (e.g. 22, 24). Does not affect the action''s own Node runtime.'
default: '22'
compress-node:
description: 'pkg''s bundled-binary compression: Brotli | GZip | Zstd | None. Zstd requires Node.js >= 22.15 on the build host.'
default: 'None'
fallback-to-source:
description: 'Pass pkg --fallback-to-source for bytecode-fabricator failures.'
default: 'false'
public:
description: 'Pass pkg --public (ships sources as plaintext).'
default: 'false'
public-packages:
description: 'Comma-separated package names to mark public (pkg --public-packages).'
options:
description: 'Comma-separated V8 options baked into the binary (pkg --options).'
no-bytecode:
description: 'Pass pkg --no-bytecode.'
default: 'false'
no-dict:
description: 'Comma-separated list of packages for pkg --no-dict (or * for all).'
debug:
description: 'Pass pkg --debug.'
default: 'false'
extra-args:
description: 'Raw extra flags appended to the pkg CLI invocation.'
pkg-version:
description: 'npm version specifier for @yao-pkg/pkg (e.g. ~6.16.0). Bypassed when pkg-path is set.'
default: '~6.16.0'
description: 'npm version specifier for @yao-pkg/pkg (e.g. ~6.19.0). 6.19.0+ is required for the full build-flag surface in pkg config (compress, fallbackToSource, public, publicPackages, options, bytecode, nativeBuild, noDictionary, debug, signature). Bypassed when pkg-path is set.'
default: '~6.19.0'
pkg-path:
description: 'Absolute path to a pre-installed pkg binary. Skips the implicit npm i -g.'
strip:
Expand Down Expand Up @@ -169,7 +142,7 @@ runs:
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/pkg-cache
key: ${{ inputs.cache-key || format('pkg-fetch-{0}-{1}-node{2}-{3}', runner.os, runner.arch, inputs.node-version, hashFiles('**/package.json', '.pkgrc*', '**/pkg.config.{js,ts,json}')) }}
key: ${{ inputs.cache-key || format('pkg-fetch-{0}-{1}-{2}', runner.os, runner.arch, hashFiles('**/package.json', '.pkgrc*', '**/pkg.config.{js,ts,json}')) }}

- name: Install @yao-pkg/pkg
if: ${{ inputs.pkg-path == '' }}
Expand All @@ -182,19 +155,9 @@ runs:
uses: ./packages/build
with:
config: ${{ inputs.config }}
config-inline: ${{ inputs.config-inline }}
entry: ${{ inputs.entry }}
targets: ${{ inputs.targets }}
mode: ${{ inputs.mode }}
node-version: ${{ inputs.node-version }}
compress-node: ${{ inputs.compress-node }}
fallback-to-source: ${{ inputs.fallback-to-source }}
public: ${{ inputs.public }}
public-packages: ${{ inputs.public-packages }}
options: ${{ inputs.options }}
no-bytecode: ${{ inputs.no-bytecode }}
no-dict: ${{ inputs.no-dict }}
debug: ${{ inputs.debug }}
extra-args: ${{ inputs.extra-args }}
pkg-version: ${{ inputs.pkg-version }}
pkg-path: ${{ inputs.pkg-path }}
strip: ${{ inputs.strip }}
Expand Down
15 changes: 13 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ directly).
| `targets.ts` | `Target` type, `parseTarget`, `hostTarget`, `formatTarget` |
| `templates.ts` | `{name}/{version}/{os}/{arch}/…` filename renderer + token bag |
| `checksum.ts` | sha256/sha512/md5 streaming, `writeShasumsFile`, `writeSidecar` |
| `inputs.ts` | `INPUT_SPECS` (source of truth for every input) + `parseInputs` |
| `pkg-runner.ts` | `@yao-pkg/pkg` CLI bridge + `buildPkgArgs` |
| `inputs.ts` | `INPUT_SPECS` (action-layer inputs only) + `parseInputs` |
| `pkg-runner.ts` | `@yao-pkg/pkg` CLI bridge + `buildPkgArgs` (no pkg-flag mirroring) |
| `pkg-output-map.ts` | Reconciles pkg on-disk outputs to `Target[]` |
| `archive.ts` | tar.gz / tar.xz / zip / 7z writers (yazl for zip) |
| `summary.ts` | Markdown table for `GITHUB_STEP_SUMMARY` |
Expand Down Expand Up @@ -156,6 +156,17 @@ Source of truth: `packages/core/src/inputs.ts::INPUT_SPECS`. One `InputSpec`
record per input with `name / description / default? / required? / category /
deprecated? / secret?`.

**Input-surface scope** (2026-04-23): the action intentionally does not
mirror pkg's CLI. Pkg-specific knobs are expressed via the user's pkg config
file, which `buildPkgArgs` forwards through `--config`. The action owns only
concerns that pkg config cannot express: CI-matrix `targets`, the
`pkg-version` / `pkg-path` install choice, archive format, filename template,
checksum algorithms, Windows-metadata resedit patch, signing, cache, and step
summary. Rationale: decouple the action from pkg's CLI evolution — each new
pkg flag otherwise forced a back-compat-preserving input bump here.
Authoritative list of dropped inputs + migration note lives in
[`STATUS.yaml`](../STATUS.yaml) under `input-surface-slim`.

Emitted:

- `/action.yml` — top-level composite. Every input forwarded explicitly to the
Expand Down
16 changes: 3 additions & 13 deletions docs/inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,11 @@ Every `pkg-action` input, grouped by category.

| Input | Default | Required | Secret | Description |
| --- | --- | --- | --- | --- |
| `config` | — | no | no | Path to a pkg config (.pkgrc, pkg.config.{js,ts,json}, or package.json). Auto-detected when omitted. |
| `config` | — | no | no | Path to a pkg config (.pkgrc, pkg.config.{js,ts,json}, or package.json). Auto-detected when omitted. Mutually exclusive with config-inline. |
| `config-inline` | — | no | yes | Pkg config as a JSON string. Written to a temp file and passed to pkg via --config. Mutually exclusive with config. Registered with core.setSecret so exact matches are redacted from logs; still written to a temp file on the runner, so prefer config for anything beyond trivial knobs. |
| `entry` | — | no | no | Entry script when not specified in the config. |
| `targets` | — | no | no | Comma- or newline-separated pkg target triples, e.g. node22-linux-x64,node22-macos-arm64. Defaults to the host target. |
| `mode` | `standard` | no | no | standard \| sea — selects pkg Standard or SEA mode. |
| `node-version` | `22` | no | no | pkg's bundled Node.js major (e.g. 22, 24). Does not affect the action's own Node runtime. |
| `compress-node` | `None` | no | no | pkg's bundled-binary compression: Brotli \| GZip \| Zstd \| None. Zstd requires Node.js >= 22.15 on the build host. |
| `fallback-to-source` | `false` | no | no | Pass pkg --fallback-to-source for bytecode-fabricator failures. |
| `public` | `false` | no | no | Pass pkg --public (ships sources as plaintext). |
| `public-packages` | — | no | no | Comma-separated package names to mark public (pkg --public-packages). |
| `options` | — | no | no | Comma-separated V8 options baked into the binary (pkg --options). |
| `no-bytecode` | `false` | no | no | Pass pkg --no-bytecode. |
| `no-dict` | — | no | no | Comma-separated list of packages for pkg --no-dict (or * for all). |
| `debug` | `false` | no | no | Pass pkg --debug. |
| `extra-args` | — | no | no | Raw extra flags appended to the pkg CLI invocation. |
| `pkg-version` | `~6.16.0` | no | no | npm version specifier for @yao-pkg/pkg (e.g. ~6.16.0). Bypassed when pkg-path is set. |
| `pkg-version` | `~6.19.0` | no | no | npm version specifier for @yao-pkg/pkg (e.g. ~6.19.0). 6.19.0+ is required for the full build-flag surface in pkg config (compress, fallbackToSource, public, publicPackages, options, bytecode, nativeBuild, noDictionary, debug, signature). Bypassed when pkg-path is set. |
| `pkg-path` | — | no | no | Absolute path to a pre-installed pkg binary. Skips the implicit npm i -g. |

## Post-build
Expand Down
37 changes: 5 additions & 32 deletions packages/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,16 @@ author: 'yao-pkg contributors'

inputs:
config:
description: 'Path to a pkg config (.pkgrc, pkg.config.{js,ts,json}, or package.json). Auto-detected when omitted.'
description: 'Path to a pkg config (.pkgrc, pkg.config.{js,ts,json}, or package.json). Auto-detected when omitted. Mutually exclusive with config-inline.'
config-inline:
description: 'Pkg config as a JSON string. Written to a temp file and passed to pkg via --config. Mutually exclusive with config. Registered with core.setSecret so exact matches are redacted from logs; still written to a temp file on the runner, so prefer config for anything beyond trivial knobs.'
entry:
description: 'Entry script when not specified in the config.'
targets:
description: 'Comma- or newline-separated pkg target triples, e.g. node22-linux-x64,node22-macos-arm64. Defaults to the host target.'
mode:
description: 'standard | sea — selects pkg Standard or SEA mode.'
default: 'standard'
node-version:
description: 'pkg''s bundled Node.js major (e.g. 22, 24). Does not affect the action''s own Node runtime.'
default: '22'
compress-node:
description: 'pkg''s bundled-binary compression: Brotli | GZip | Zstd | None. Zstd requires Node.js >= 22.15 on the build host.'
default: 'None'
fallback-to-source:
description: 'Pass pkg --fallback-to-source for bytecode-fabricator failures.'
default: 'false'
public:
description: 'Pass pkg --public (ships sources as plaintext).'
default: 'false'
public-packages:
description: 'Comma-separated package names to mark public (pkg --public-packages).'
options:
description: 'Comma-separated V8 options baked into the binary (pkg --options).'
no-bytecode:
description: 'Pass pkg --no-bytecode.'
default: 'false'
no-dict:
description: 'Comma-separated list of packages for pkg --no-dict (or * for all).'
debug:
description: 'Pass pkg --debug.'
default: 'false'
extra-args:
description: 'Raw extra flags appended to the pkg CLI invocation.'
pkg-version:
description: 'npm version specifier for @yao-pkg/pkg (e.g. ~6.16.0). Bypassed when pkg-path is set.'
default: '~6.16.0'
description: 'npm version specifier for @yao-pkg/pkg (e.g. ~6.19.0). 6.19.0+ is required for the full build-flag surface in pkg config (compress, fallbackToSource, public, publicPackages, options, bytecode, nativeBuild, noDictionary, debug, signature). Bypassed when pkg-path is set.'
default: '~6.19.0'
pkg-path:
description: 'Absolute path to a pre-installed pkg binary. Skips the implicit npm i -g.'
strip:
Expand Down
Loading
Loading