* style: set Prettier print width to 110 and reformat
Without a repo config, the Prettier CLI and Trunk used the default
width of 80, while editors with a user printWidth reflowed files on
save, producing formatting-only diffs. Pin the width in .prettierrc.json
and reformat source, tests, scripts and JS/TS config files to match.
Files that already failed Prettier at width 80 for unrelated reasons
(tsconfig*.json, .trunk/*, V2_FAQ.MD) are left untouched.
No code changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: cover cache restore and save failure paths
Codecov flagged lines in #246 as uncovered patch lines; they were
untested before the reformat touched them. Add behavior tests for:
- Cache: cache miss, missing or empty manifests, unreadable cache dir,
metapackage-only restore, install scripts running after their archive
with dpkg actions, existing archives, unavailable cache service, and
save failures.
- ManifestEntry.fromJSON round trip.
- buildFileList dropping missing files, updateAptLists skipping update
for populated lists and searching only to max depth, and fromInput
ignoring a version without a package name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Describe version as a user defined value and link the README caveats by
URL, since the bare README.md reference doesn't resolve from the
Marketplace or the workflow editor.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ts-apt is now published to npm, so depend on the exact release instead
of a git commit SHA. check_deps_pin accepts an exact registry version
whose lockfile entry resolves from registry.npmjs.org with a sha512
integrity hash, and still accepts a full SHA pin for unreleased ts-apt
changes. Ranges, tags and branch refs are still rejected.
1.1.1 keeps the 1.1.0 public API; the dpkg-query listInstalled output
format change does not affect the action, which doesn't call it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix: find install scripts for package names with regex metacharacters
findInstallScript built a regex from the raw package name, so names like
g++ or libstdc++6 threw "Nothing to repeat" on cache save and on restore
with install scripts, and a '.' in a name could match another package's
script. Compare file names as plain strings instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf: speed up cache restores
- Upload the diagnostic artifact only when debug is on or the action
fails. The upload took ~1.4s of a ~3.7s restore in CI.
- Extract all package archives in one sudo shell when install scripts
are off, instead of a sudo and tar process per package. Per-package
extraction stays when scripts run, to keep install ordering (#196).
- Skip the apt-fast availability probe on a cache hit, where nothing is
installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: document Manifest and ManifestEntry public members
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Replace the hand-rolled .githooks/ directory and core.hooksPath prepare
script with husky, matching ts-apt. The pre-push hook moves to
.husky/pre-push and still runs npm run check:deps-pin.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* build(deps): pin ts-apt to v1.1.0-rc.1
The previous pin, 342c7d0, was the ts-apt v2-support head before that
branch was rewritten, so no ts-apt branch or tag contains it anymore.
Pin 67d450e, the commit released as ts-apt v1.1.0-rc.1 on staging.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* build(deps): pin ts-apt to v1.1.0
Merging main-2 into this branch restored the unreachable 342c7d0 pin in
package.json while package-lock.json kept 67d450e. Pin both to 64aaab8,
the commit released as ts-apt v1.1.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Replace scripts/ops/check_tsapt_pin.sh with scripts/ops/check_deps_pin.mts,
which takes dependency names as arguments and checks each one for a
local symlink, a full commit SHA pin in package.json, and a matching,
non-link package-lock.json entry.
Rename the npm script to check:deps-pin (run with ts-apt) and use it from
the pre-push hook, the CI action-deps-pinned job and AGENTS.md.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore: remove unused dist directory
* build: remove unused dependencies and address npm audit findings
Remove dependencies nothing imports or runs: class-transformer, tar and
@types/tar (archives use system tar), internet-message,
@devcontainers/cli, @typescript-eslint/eslint-plugin, typedoc and
typedoc-plugin-markdown (with the dead docs:api script). Move rollup to
devDependencies since it is only used to build the bundle.
Update dependencies within their declared ranges and add scoped
overrides: glob 9.0.0 - 14.0.1 to ^14.0.4, and minimatch ^11.0.5 for
eslint, @typescript-eslint/parser and type-coverage only. minimatch is
not overridden for @actions/glob because minimatch 11 removed matchOne,
which @actions/glob calls (breaking cache hashFiles).
npm audit goes from 66 (2 critical, 10 high) to 39 (0 critical, 0 high).
Remaining: minimatch inside @actions/glob (production), npm's bundled
dependencies under @semantic-release/npm, and deepmerge (no fix).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add scripts/ops/check_tsapt_pin.sh, which fails when node_modules/ts-apt
is a symlink, package.json does not pin ts-apt to a full commit SHA, or
package-lock.json resolves ts-apt to a link or a different SHA.
Run it from a .githooks/pre-push hook (installed by the npm prepare
script via core.hooksPath) and from the CI action-deps-pinned job, and
document the rule in AGENTS.md.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ci: bundle the action with rollup and upload it for integration jobs
The integration jobs download an action-bundle-<sha> artifact that
nothing built or uploaded, so all of them failed on their first step.
v2 also had no bundle: action.yml runs dist/index.js, and an action has
no node_modules at runtime.
- Add rollup.config.mjs, which bundles the tsc output (dist/src) into a
self-contained dist/index.js. npm run build now runs it.
- Fix the setup action, which never built: with if_no_artifact_found:
ignore the download step always succeeds, so check found_artifact.
Download cached builds into dist/, and overwrite the build artifact
since several jobs in a run can build it.
- Upload action.yml and the bundle as action-bundle-<sha> from
setup-and-cache.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chor: remove stale file
* fix: write logs under the home cache directory and await artifact upload
Instruments opened its log files at the relative path cache-apt-pkgs/
before that directory existed, so the action crashed at startup with
ENOENT. It also pointed artifact uploads there while Cache uses
~/cache-apt-pkgs. Resolve both with a shared resolveCacheDir helper and
create the directory before opening the logs.
The artifact upload was also never awaited, so a failed upload became an
unhandled rejection. Await it and report failures as a warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: ship ts-apt's apt-fast script with the bundle
On runners with aria2 installed, ts-apt uses apt-fast and spawns
scripts/dist/apt-fast.sh relative to its own module. Bundling moved that
module, so the action failed with "spawn .../scripts/dist/apt-fast.sh
ENOENT".
Write the bundle to dist/action/index.js and copy ts-apt's scripts/dist
to dist/scripts/dist, which is where ts-apt looks from there. Point
action.yml at the new entrypoint and include both in the action bundle
artifact. Workflow artifacts drop file modes, so the action marks the
script executable at startup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* build: pin ts-apt to e627c3b for install timeout and dpkg-query fixes
ts-apt killed every apt command after 30 seconds, so installs failed
with exit code 124, and listInstalledFiles passed name=version to
dpkg-query -L, which rejects it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: make diagnostic artifact names unique per step
Every job in a run uploaded capa_artifacts-<run id>, so only the first
upload succeeded. Add the run attempt, job and step IDs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci: run cache restore jobs after the jobs that create the cache
The restore and add-package jobs all only needed testing, so they ran in
parallel with standard_workflow_install and raced on the same cache key.
Restore the ordering v1 uses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci: authenticate the action pin check to avoid API rate limits
The check called the GitHub API anonymously and hit the rate limit for
the shared runner IP. The script uses GITHUB_TOKEN when it's set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* build: pin ts-apt to 342c7d0 for native arch and stdin input
Adds PackageManager.getNativeArchitecture and CommandOptions.input, used by the version-list outputs and archive creation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: report requested and dependency package versions separately
package-version-list held every installed package and both outputs were
identical. Outputs also carried arch suffixes inconsistently and followed
install order.
- package-version-list lists only requested packages; all-package-version-list
lists every installed package.
- Entries are name=version, with :arch only for packages installed for a
non-native architecture (e.g. libc6:i386), so native output matches v1.
- Entries are sorted with PackageName.compareTo.
- Requested packages are matched by name, and by arch when both specify one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix: archive and restore packages with system tar under sudo
The in-process tar library ran as the runner user, so archiving failed on
root-only files such as /etc/default/cacerts from ca-certificates-java
(regression #72), and extraction could skip root-owned paths without failing.
- Create and extract with sudo tar; the file list is piped NUL-separated on
stdin so large packages don't exceed the argument size limit.
- Keep directory symlinks such as /lib on extract.
- Hand archives back to the runner user for the cache save.
- Disable the command timeout for tar and maintainer scripts.
- Record the installed (versioned) entries in manifest_main.json; it held
unversioned names with no files.
- Return every installed package on a cache hit so outputs include deps.
- Split runInstallScripts into per-script runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci: update package expectations for Ubuntu 24.04
libasound2 is libasound2t64 on 24.04 (regression #81), and gir1.2-gdkpixbuf-2.0 moved to 2.42.10+dfsg-3ubuntu3.3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: add ts-apt boundary rules to AGENTS.md
APT and dpkg logic belongs in ts-apt; ts-apt changes are pushed before the action bumps its pin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci: pin version-list jobs to ubuntu-24.04 and fix spaces-in-paths tests
- list_versions / list_all_versions: run on ubuntu-24.04. The expected
versions are from 24.04, but ubuntu-latest now resolves newer ones
(e.g. rolldice=1.16-1build4, xdot=1.6-1).
- regression_76: cache libopenal-data ("Default HRTF.mhr") instead of
Intel oneAPI. Drops the Intel repo and the unused opencl and
libsundials-dev installs (188 MB, 155 packages), which made the job
take 75+ minutes when the Ubuntu mirror was slow. Cap at 15 minutes.
- regression_81: list alsa-ucm-conf explicitly so the test caches a
package with spaces in its file paths.
Same fixes as #235 and #236 on master.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- eslint: parse scripts/**/*.{ts,mts} with the TypeScript parser.
Only src and test were configured, so the scripts failed to parse.
- action-deps-pinned: run the --scan check over .github. The step passed
./action.yml, which the script doesn't accept, and action.yml has no
uses: dependencies to check anyway.
- npm audit fix for the high severity advisories in production
dependencies (undici, brace-expansion) and @actions/artifact 6.3.1.
The remaining moderate glob/minimatch advisories need a breaking
@actions/artifact downgrade, and the audit step only fails on high.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* build: install ts-apt from GitHub instead of a local path
package.json pointed ts-apt at file:../ts-apt, which only exists on a
dev machine, so every test suite in CI failed with "Cannot find package
'ts-apt'". ts-apt isn't on npm, so pin it to a commit on GitHub. Its
prepare script builds dist on install.
The dev:tsapt:link script used --save, which is how the local path got
committed. Use --no-save so linking a local ts-apt doesn't change
package.json, and restore with npm ci on unlink.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: clear GITHUB_RUN_ID when testing the run ID fallback
GitHub Actions always sets GITHUB_RUN_ID, so the fallback test failed
once CI could run the tests at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Manifest.from built every entry with an empty file list and buildFileList
was never called, so tar.create was handed no paths. node-tar throws "no
paths specified to add to archive" for that, so every cache miss failed
before saving.
The runner now builds each manifest entry from buildFileList and passes
the manifest to archiveAndSave. Packages with no files (e.g.
metapackages) are skipped on archive and on restore since tar can't
create an empty archive. Manifest.from is removed.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Restore extracted archives sorted by filename, so packages whose install
scripts depend on files from other packages could break. Restore now
follows the entry order in manifest_all.json, which comes from the apt
install output, and fails explicitly when an archive is missing.
Refs #196
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add an .actrc, runner Dockerfile, push event payload, and
scripts/dev/act_ci.sh to run ci.yml jobs locally with nektos/act.
The script refuses --bind/--reuse in .actrc, since a bound checkout
runs as root and rewrites ownership and modes in the working tree.
Document the @latest-v2 version label in the README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pull dev upstream to staging. (#112)
* Use awk to enclose filename in single quotes tar #99
* Add null field separator so filenames don't get broken up.
* Move upload logs up in the action sequence so it captures data before it gets deleted.
* Fix awk (#109)
---------
Co-authored-by: sn-o-w <cristian.silaghi@mozilla.ro>
* Fix awk delimiter.
Pull in fix by @sn-o-w in d0ee83b497 mentioned in issue #99
* Swap out Bash based APT query logic for Golang version. (#117)
* First version of a Golang version of command handling in general. (#118)
---------
Co-authored-by: sn-o-w <cristian.silaghi@mozilla.ro>
* Pull dev upstream to staging. (#112)
* Use awk to enclose filename in single quotes tar #99
* Add null field separator so filenames don't get broken up.
* Move upload logs up in the action sequence so it captures data before it gets deleted.
* Fix awk (#109)
---------
Co-authored-by: sn-o-w <cristian.silaghi@mozilla.ro>
* Fix awk delimiter.
Pull in fix by @sn-o-w in d0ee83b497 mentioned in issue #99
---------
Co-authored-by: sn-o-w <cristian.silaghi@mozilla.ro>
* fix: apt cache performance
Use a single call to apt-cache to reduce the time needed to lookup
package versions.
Also:
* Added millisecond details to log timing so slow operations can be more
easily identified.
* Perform apt update before determining package versions.
Fixes#103
* chore: descriptive variable names and use log_err
Added the review feedback, updating variable names to be more
descriptive and using log_err where appropriate.