Fix downloaded project ZIP using absolute paths (#147)#356
Open
cscheid wants to merge 2 commits into
Open
Conversation
The hub-client "Export ZIP" feature produced archives whose entries were absolute (e.g. /cscheid/columns.qmd), because exportProjectAsZip used the stored index paths verbatim and those carry a leading slash. unzip refuses absolute paths and prints "stripped absolute path spec" for every entry. Fix at the ZIP serialization boundary (not the storage convention, which is load-bearing across the sync client and index): - export-zip.ts: exportProjectAsZip(client, rootDir?) now strips leading slashes so entries are relative, and nests every entry under a single sanitized top-level folder when rootDir is given. The importer already strips a common leading directory, so export/import round-trip cleanly. - New project-folder-name.ts helper (exported from quarto-sync-client): derives a safe single path segment from the project name — Windows-hostile chars, path separators, and control chars collapse to hyphens; trailing dots/spaces are trimmed; empty falls back to "project". - automergeSync.ts wrapper forwards an optional rootDir. - ProjectTab.tsx computes one slug via projectFolderName(project.description) and uses it for both the in-archive folder and the .zip download filename, so the two can never drift. Tests: new project-folder-name.test.ts (9 cases), export-zip round-trip and absolute-path cases, and a jsdom ProjectTab.test.tsx for the slug wiring. Verified end-to-end with the real function + system `unzip -l`: entries now read Demo-Playground/cscheid/columns.qmd with no warnings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #147. The hub-client Export ZIP feature produced archives whose entries were absolute (e.g.
/cscheid/columns.qmd), becauseexportProjectAsZipused the stored index paths verbatim and those carry a leading slash.unziprefuses absolute paths and printswarning: stripped absolute path spec …for every entry.Now entries are relative and nested under a single project-name folder, matching the download filename:
No warnings, one tidy
Demo-Playground/root.Approach
The fix lives at the ZIP serialization boundary, not the storage convention (the leading-slash index paths are load-bearing across the sync client and index, so changing them is out of scope and risky):
export-zip.ts—exportProjectAsZip(client, rootDir?)strips leading slashes so entries are relative, and nests every entry under a sanitized top-level folder whenrootDiris given. The importer (parseProjectZip) already strips a common leading directory, so export/import round-trip cleanly.project-folder-name.ts(new, exported fromquarto-sync-client) — derives a safe single path segment from the project name: Windows-hostile chars (< > : " / \ | ? *), path separators, and control chars collapse to hyphens; trailing dots/spaces are trimmed; empty falls back toproject.automergeSync.ts— wrapper forwards an optionalrootDir.ProjectTab.tsx— computes one slug viaprojectFolderName(project.description)and uses it for both the in-archive folder and the.zipdownload filename, so they can never drift.Tests
project-folder-name.test.ts— 9 cases (sanitization, fallback, leading/trailing trim).export-zip.test.ts— leading-slash stripping,rootDirnesting, no absolute entries,rootDirnormalization, and an export→import round-trip.ProjectTab.test.tsx— jsdom wiring: same sanitized slug drives bothonExportZipand the download filename.All pass: quarto-sync-client 117, preview-runtime 74, hub-client 675 unit + 76 integration + 121 wasm. Production build (
tsc -b && vite build) clean.End-to-end verification
Ran the real
exportProjectAsZip(client, 'Demo Playground')with absolute stored paths and inspected the bytes with the systemunzip(the bug reporter's exact command) — entries are relative, nested underDemo-Playground/, and extraction emits no "stripped absolute path spec" warnings. The space in "Demo Playground" is sanitized toDemo-Playground, matching the download filename.Not run this session (noted as optional follow-ups in the plan): a live-browser hub session and a Playwright export e2e (there is no export e2e today).
🤖 Generated with Claude Code