Compare commits

...

4 Commits

Author SHA1 Message Date
jknapp 876ba8a8fc Merge pull request 'Nest workspace under workspace/ in project backup' (#9) from fix/backup-workspace-nesting into main
Build App / compute-version (push) Successful in 2s
Build App / build-macos (push) Successful in 2m22s
Build App / build-windows (push) Successful in 4m36s
Build App / build-linux (push) Successful in 5m5s
Build App / create-tag (push) Successful in 3s
Build App / sync-to-github (push) Successful in 12s
2026-07-01 13:23:08 +00:00
shadow-test 5cd528a4ef Use flags=rh so intra-workspace hardlinks survive the transform
Build App / compute-version (pull_request) Successful in 3s
Build App / build-macos (pull_request) Successful in 2m15s
Build App / build-windows (pull_request) Successful in 4m24s
Build App / build-linux (pull_request) Successful in 5m3s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
Review caught that `flags=r` disables rewriting of both symlink AND
hardlink target names. Leaving symlink targets alone is intended, but a
hardlink's stored target is an archive-internal reference to another
member's name — when member names become `workspace/...` but the
hardlink target stays `./hard_link`, extraction fails hard:

  tar: workspace/file.txt: Cannot hard link to './hard_link':
       No such file or directory

`flags=rh` rewrites regular member names and hardlink target names
together (keeping the pair consistent) while still leaving symlink
targets untouched. Verified in-container: extract exit 0, symlink target
preserved, hardlink pair shares one inode, nesting under workspace/ intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 06:22:14 -07:00
shadow-test c3fc029b1d Nest workspace under workspace/ in project backup
Build App / compute-version (pull_request) Successful in 4s
Build App / build-macos (pull_request) Successful in 2m17s
Build App / build-linux (pull_request) Successful in 5m7s
Build App / build-windows (pull_request) Successful in 5m9s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
The backup archive placed the workspace at the archive root (`./...`)
while the sanitized home config sat under `home-claude/`. On extraction
the workspace files scattered loose into the extraction directory and
only `home-claude/` showed up as a distinct folder, so the backup read
as "config only, workspace missing" — and some archive viewers didn't
surface the root-level entries at all.

Add `--transform='flags=r;s,^\./,workspace/,'` so the workspace nests
under `workspace/`, parallel to `home-claude/`. `flags=r` scopes the
rewrite to member names only, leaving symlink targets (relative and
absolute) intact. Excludes still match the pre-transform names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 06:18:08 -07:00
jknapp dc253e8da0 Merge pull request 'Fix backend-switch AWS auth + add /workspace backup and terminal file drag-and-drop' (#8) from fix/backend-switch-aws-creds into main
Build App / compute-version (push) Successful in 3s
Build Container / build-container (push) Successful in 34s
Build App / build-macos (push) Successful in 2m23s
Build App / build-windows (push) Successful in 3m14s
Build App / build-linux (push) Successful in 6m22s
Build App / create-tag (push) Successful in 5s
Build App / sync-to-github (push) Successful in 10s
2026-06-30 22:20:00 +00:00
+8 -1
View File
@@ -155,7 +155,7 @@ pub async fn download_container_file(
/// Create a `.tar.gz` backup of the container and stream it to a host file. /// Create a `.tar.gz` backup of the container and stream it to a host file.
/// The archive contains: /// The archive contains:
/// - the workspace (default /workspace), minus regenerable build artifacts /// - the workspace (default /workspace), minus regenerable build artifacts
/// (node_modules, target), at the archive root, and /// (node_modules, target), under `workspace/`, and
/// - a sanitized copy of the home config under `home-claude/`: ~/.claude.json /// - a sanitized copy of the home config under `home-claude/`: ~/.claude.json
/// with secret-bearing keys removed (mcpServers/settings kept) and ~/.claude/ /// with secret-bearing keys removed (mcpServers/settings kept) and ~/.claude/
/// minus the OAuth `.credentials.json`, so MCP servers, settings and skills /// minus the OAuth `.credentials.json`, so MCP servers, settings and skills
@@ -204,6 +204,12 @@ pub async fn download_container_backup(
// transient unreadable file from aborting the whole backup. If jq can't // transient unreadable file from aborting the whole backup. If jq can't
// parse ~/.claude.json we substitute an empty object — never the raw file — // parse ~/.claude.json we substitute an empty object — never the raw file —
// so secrets can't leak through the sanitization fallback. // so secrets can't leak through the sanitization fallback.
// The `--transform` nests the workspace under `workspace/` (parallel to
// `home-claude/`) so an extracted archive has both clearly labeled instead
// of scattering the workspace files into the extraction dir. `flags=rh`
// rewrites regular member names AND hardlink target names (so an intra-
// workspace hardlink pair still resolves on extract) while leaving symlink
// targets untouched (rewriting those would corrupt relative/absolute links).
let script = r#"set -e let script = r#"set -e
STAGE=$(mktemp -d) STAGE=$(mktemp -d)
trap 'rm -rf "$STAGE"' EXIT trap 'rm -rf "$STAGE"' EXIT
@@ -221,6 +227,7 @@ if [ -d "$HOME/.claude" ]; then
fi fi
tar czf - --ignore-failed-read \ tar czf - --ignore-failed-read \
--exclude='*/node_modules' --exclude='*/target' \ --exclude='*/node_modules' --exclude='*/target' \
--transform='flags=rh;s,^\./,workspace/,' \
-C "$TC_BACKUP_SRC" . \ -C "$TC_BACKUP_SRC" . \
-C "$STAGE" home-claude"#; -C "$STAGE" home-claude"#;