fix(bundler): accept .zip archives produced by apm pack (apm 0.20+)#47
fix(bundler): accept .zip archives produced by apm pack (apm 0.20+)#47danielmeppiel wants to merge 1 commit into
Conversation
apm 0.20 changed `apm pack --archive` to emit a `.zip` archive by default (microsoft/apm#1720). The action's bundle-detection paths still assumed `.tar.gz`, so against apm 0.20+ they silently failed: - `findBundleOrNull` filtered `entries.endsWith('.tar.gz')`, found zero matches for the produced `.zip`, returned null, and the pack step errored "apm pack produced no bundle" (the GH-AW Compatibility job in microsoft/apm release run 27394583646). - `detectBundleFormat` listed contents with `tar tzf`, which cannot read a `.zip` on GNU tar (Linux runners), failing before the format gate. - The `bundles-file` (multibundle restore) parser hard-rejected any entry not ending in `.tar.gz`. Fix: accept BOTH `.zip` and `.tar.gz` on the detection side rather than pinning `--archive-format` on the pack call. The action supports a range of apm versions via `apm-version`; pinning the flag would break older CLIs that do not know it. A new `listArchiveEntries` helper is format-aware (`unzip -Z1` for `.zip`, `tar tzf` for `.tar.gz`); both runners ship `unzip` and its short listing matches the existing marker regexes. Scope note: the `extractBundle` tar-fallback (reached only when apm is unavailable -- a near-dead path, since the primary `apm unpack` handles .zip natively) still extracts `.tar.gz` only. `unzip` has no `--strip-components`/exclude equivalent, so zip-aware fallback extraction is deferred with an in-code NOTE. Tests: +4 regression traps (single .zip found; .zip+.tar.gz ambiguity; .zip format detection via unzip -Z1; multibundle .zip acceptance), each proven by the mutation-break gate. dist/ rebuilt via ncc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
shepherd-driver advisory (terminal pass)Driven to a terminal ready-to-merge state. No Folded in this run
Deferred (out-of-scope follow-up)
Regression-trap evidence (mutation-break gate)
Gate (substitutes the ruff lint contract for this repo)
CIAll required checks observed green on head Mergeability status
Convergence1 outer iteration; 0 Copilot rounds. Ready for maintainer review. |
There was a problem hiding this comment.
Pull request overview
This PR updates the action’s bundle detection logic to remain compatible with apm pack --archive output across the supported apm-version range, specifically handling the apm 0.20+ default switch from .tar.gz to .zip archives.
Changes:
- Add format-aware archive table-of-contents listing (
unzip -Z1for.zip,tar tzffor.tar.gz) and use it fordetectBundleFormat. - Generalize bundle discovery to accept both
.zipand.tar.gzwhen locating packed archives. - Update multibundle parsing, tests, and input documentation to recognize
.ziparchives.
Show a summary per file
| File | Description |
|---|---|
| src/bundler.ts | Adds .zip-aware archive entry listing and broadens archive discovery to .zip/.tar.gz. |
| src/multibundle.ts | Allows .zip entries in bundles-file parsing (previously .tar.gz only). |
| src/tests/bundler.test.ts | Adds regression tests for .zip format detection and archive selection. |
| src/tests/multibundle.test.ts | Adds tests ensuring .zip entries are accepted and non-archives rejected. |
| action.yml | Updates input docs to reflect .zip default behavior on apm 0.20+. |
| dist/index.js | Rebuilt compiled action output reflecting the updated bundler behavior. |
| dist/bundler.d.ts | Updates typings/docs for the new archive listing approach. |
| dist/970.index.js | Rebuilt compiled chunk reflecting multibundle parsing updates. |
| dist/multibundle.d.ts | Updates typings/docs for .zip/.tar.gz acceptance. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 5/9 changed files
- Comments generated: 2
| async function listArchiveEntries(bundlePath: string): Promise<string[]> { | ||
| const isZip = bundlePath.endsWith('.zip'); | ||
| const cmd = isZip ? 'unzip' : 'tar'; | ||
| const args = isZip ? ['-Z1', bundlePath] : ['tzf', bundlePath]; | ||
| const list = await exec.getExecOutput(cmd, args, { |
| const archives = entries | ||
| .filter(e => ARCHIVE_EXTENSIONS.some(ext => e.endsWith(ext))) | ||
| .sort(); |
TL;DR
apm 0.20 made
apm pack --archiveemit a.ziparchive by default (microsoft/apm#1720). The action's bundle-detection paths still assumed.tar.gz, so against apm 0.20+ they silently broke. This PR makes detection accept both.zipand.tar.gzwithout pinning--archive-format, keeping the action compatible across the fullapm-versionrange it supports.Problem (WHY)
Observed in the microsoft/apm v0.20.0 release run
27394583646-- the GH-AW Compatibility job failed withapm pack produced no bundle.apm packitself succeeded and producedinline-workflow-1.0.0.zip; the action then could not find it.Three independent surfaces in this repo still assumed
.tar.gz:findBundleOrNull(src/bundler.ts)entries.endsWith('.tar.gz')-> 0 matches for the.zip-> returnednull-> "apm pack produced no bundle" (the failing job)detectBundleFormat(src/bundler.ts)tar tzf, which cannot read a.zipon GNU tar (Linux runners) -> threw before the format gateparseBundleListFile(src/multibundle.ts)bundles-fileentry not ending in.tar.gzApproach (WHAT)
Accept both extensions on the detection side; do NOT pin
--archive-formaton the pack call. The action installs a configurable apm version (apm-version, default0.14.0, orlatest). Pinning--archive-format tar.gzwould break older CLIs that do not know the flag. The version-robust fix is to recognize whatever extensionapm packemits.Implementation (HOW)
listArchiveEntries(bundlePath)helper -- format-aware table-of-contents:unzip -Z1for.zip(zipinfo short listing, one entry per line),tar tzffor.tar.gz. Both GitHub-hosted Linux and macOS runners shipunzip, and its output shape (wrapper/apm.lock.yaml) matchestar tzf, so the existing marker regexes are unchanged.detectBundleFormatnow delegates tolistArchiveEntries.findBundleOrNullmatches a module-levelARCHIVE_EXTENSIONS = ['.zip', '.tar.gz']; the multiple-archives error generalized to "Multiple bundle archives found...".parseBundleListFileaccepts.zipor.tar.gzentries.action.ymlinput docs (pack,archive,bundles-file) updated to reflect.zip-by-default on apm 0.20+.Trade-offs / deferred
extractBundletar-fallback (tar xzf) is reached only when apm is unavailable -- a near-dead path, since the primaryapm unpackhandles.zipnatively.unziphas no--strip-components/exclude equivalent, and the flatten+exclude logic needs its own surface and tests, so zip-aware fallback extraction is deferred with an in-codeNOTE.detectBundleFormat(the unconditional gate that runs before the apm-available check) andfindBundleOrNull(the failing job) are fixed because they break in normal operation.apm-versionto a.zip-emitting release is intentionally out of scope -- the pinned default protects downstream workflows by design.Validation evidence
npm run typecheck-- cleannpm run lint(eslint) -- cleannpm test-- 186 passed (was 182; +4 new regression traps)npm run build(ncc) --dist/rebuilt and committed (CI enforces dist freshness)ARCHIVE_EXTENSIONSto tar-only;isZiptofalse; multibundle to tar-only) and confirmed exactly the 4 new tests fail, then restored.How to test
Or end-to-end: run this action with
pack: 'true'andapm-version: '0.20.0'(orlatest) and confirmbundle-pathresolves to the produced.zip.