Skip to content

fix(apps): stage pulls before replacing destination - #233

Open
Gautam Sharma (GautamSharma99) wants to merge 1 commit into
langchain-ai:mainfrom
GautamSharma99:fix/apps-pull-staging-225
Open

fix(apps): stage pulls before replacing destination#233
Gautam Sharma (GautamSharma99) wants to merge 1 commit into
langchain-ai:mainfrom
GautamSharma99:fix/apps-pull-staging-225

Conversation

@GautamSharma99

Copy link
Copy Markdown
Contributor

Summary

  • extract pulled app source into a temporary sibling directory
  • validate the complete archive and write .langsmith/app.json before touching the destination
  • replace the destination only after staging succeeds
  • keep a rollback backup if the final rename fails
  • clean up staging directories on all pre-install failures

Closes #225.

Problem

apps pull previously performed these operations in this order:

  1. confirm replacement;
  2. remove the existing app directory;
  3. create the replacement directory;
  4. validate and extract the downloaded archive;
  5. write the app link.

Any invalid gzip/base64 payload, unsafe archive entry, empty archive, extraction I/O failure, or app-link write failure could therefore destroy a known-good local app and leave no complete replacement.

Implementation

The command now creates a temporary sibling directory named like .<app>.pull-* and performs all validation/extraction/link writing there. The existing destination remains untouched while these operations run.

Once staging is complete, replaceStagedApp:

  1. moves an existing destination to a temporary sibling backup;
  2. renames the complete staging directory into the destination path;
  3. rolls the backup back into place if the staged rename fails;
  4. removes the backup only after the replacement succeeds.

The staging directory is removed by deferred cleanup whenever ownership has not been transferred to the replacement operation. A failed archive never reaches the replacement phase.

Behavior

With an existing app and an invalid server archive:

$ langsmith apps pull my-app --force
the stored source archive is not a valid gzip file: ...

The command fails, the old app files remain unchanged, and no temporary staging directory is left behind.

With a valid archive, the destination is still replaced rather than merged: files removed upstream do not remain locally, while the complete new app and .langsmith/app.json are installed together.

Tests

Added regression coverage for:

  • malformed archive with an existing destination preserves all old files
  • unsafe traversal archive with an existing destination preserves all old files
  • traversal entries never escape the working directory
  • staging directories are cleaned after invalid-archive failure
  • existing successful --force replacement still removes stale files
  • confirmed non-empty replacement continues to work
  • normal name resolution and app-link creation remain intact

The existing lower-level extraction tests continue to cover path validation, symlink skipping, non-gzip bodies, and push-to-pull archive round trips.

Local verification:

  • focused apps pull regression tests
  • go test ./...
  • go test -race ./...
  • go vet ./...
  • make build
  • git diff --check

make lint was not available locally because golangci-lint is not installed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

apps pull destroys the destination before archive validation succeeds

1 participant