Skip to content

feat: server-side cp/mv and new objects set-access command#99

Merged
designcode merged 1 commit into
mainfrom
fix/mv-command
May 11, 2026
Merged

feat: server-side cp/mv and new objects set-access command#99
designcode merged 1 commit into
mainfrom
fix/mv-command

Conversation

@designcode
Copy link
Copy Markdown
Collaborator

@designcode designcode commented May 11, 2026

Closes TIG-8731.

Summary

Adopt the new @tigrisdata/storage primitives (copy, move, setObjectAccess) so the CLI no longer streams object bytes through the client for remote↔remote operations. Same-bucket mv becomes metadata-only via X-Tigris-Rename: true; cross-bucket cp/mv use server-side CopyObject.

Changes

  • tigris cp (remote→remote) — one copy(src, dest, { srcBucket, destBucket }) call replaces the prior head + get + put flow. Server-side CopyObject, no client traffic, source metadata preserved.
  • tigris mv (remote→remote) — split:
    • Same-bucket → move(...) (metadata-only rename, one round-trip).
    • Cross-bucket → copy(...) + remove(...) (server-side, no client traffic). The server doesn't support cross-bucket move atomically; the SDK enforces this.
  • New tigris objects set-access <bucket> <key> --access command using setObjectAccess. Dedicated home for the ACL operation that was previously mixed into objects set.
  • tigris objects set marked deprecated: true with an onDeprecated message redirecting to tigris objects set-access (ACL) and tigris mv (rename). Implementation switched off the SDK-deprecated updateObject to move + setObjectAccess.
  • Folder marker semantics preserved — zero-byte objects ending in / continue to use the put('') + remove() branch in both cp and mv (CopyObject on trailing-slash keys is ambiguous across S3 implementations).

Acceptance criteria (TIG-8731)

  • Same-bucket tigris mv is metadata-only (no bytes through client).
  • Cross-bucket tigris mv uses server-side copy + remove (no bytes through client).
  • Same fix applied to cp.ts cross-bucket path.
  • Folder-marker semantics preserved.
  • mv -r parallelism (default 32) — follow-up PR, independent CLI work.
  • --json per-key failure reporting — follow-up PR, same loops.

Test plan

  • npm run lint and npm run format:check pass
  • npm test passes (659 unit / spec tests)
  • npm run build succeeds
  • Integration tests added:
    • Cross-bucket cp keeps source, creates destination
    • Cross-bucket mv removes source, creates destination
    • objects set-access --access public/private succeeds
    • objects set-access without --access exits non-zero

🤖 Generated with Claude Code


Note

Medium Risk
Changes core cp/mv object-transfer behavior to rely on new server-side storage primitives, which could affect data movement semantics (especially cross-bucket moves and folder markers) if the SDK/server behavior differs from the previous stream-based implementation.

Overview
Switches remote→remote cp and mv to use new @tigrisdata/storage server-side primitives so object bytes no longer stream through the CLI. cp now uses copy() (with a put('') fallback for folder markers), and mv uses move() for same-bucket renames and copy() + remove() for cross-bucket moves.

Adds a new objects set-access command backed by setObjectAccess, and marks objects set as deprecated in specs.yaml; objects set is reimplemented to rename via move() first (when --new-key is provided) and then update ACL via setObjectAccess.

Bumps @tigrisdata/storage to ^3.5.1 and extends integration coverage with cross-bucket cp/mv and objects set-access tests.

Reviewed by Cursor Bugbot for commit 7e124e7. Bugbot is set up for automated code reviews on this repo. Configure here.

Adopt the new @tigrisdata/storage primitives so the CLI no longer
streams object bytes through the client for remote↔remote operations.
This addresses the perf/correctness gap tracked in TIG-8731.

Changes:

- `tigris cp` (remote→remote): one `copy(src, dest, { srcBucket,
  destBucket })` call replaces the previous head + get + put flow.
  Server-side CopyObject, no client traffic, source metadata preserved.
- `tigris mv` (remote→remote): same-bucket calls use `move(...)` for
  a metadata-only rename via `X-Tigris-Rename: true` (one round-trip).
  Cross-bucket calls fall back to `copy(...)` + `remove(...)` — still
  server-side, no client traffic. The server doesn't support
  cross-bucket move atomically, hence the split.
- New `tigris objects set-access <bucket> <key> --access` command
  using `setObjectAccess`. Dedicated home for the ACL operation that
  was previously mixed into `objects set`.
- `tigris objects set` marked `deprecated: true` with an onDeprecated
  message redirecting to `tigris objects set-access` (ACL) and
  `tigris mv` (rename). Implementation switched off the deprecated
  `updateObject` to `move` + `setObjectAccess` to avoid the SDK-level
  deprecation warning.
- Folder marker semantics (zero-byte objects ending in `/`) preserved
  via the existing put('') + remove() branch in both cp and mv.

Tests:

- Unit / spec-completeness suites pick up `objects set-access`
  automatically (659 passing).
- Integration tests add a second bucket fixture and exercise the new
  paths: cross-bucket cp (source kept, dest created), cross-bucket
  mv (source gone, dest created), `objects set-access` with public/
  private and the missing-flag error.

Out of scope (follow-up): mv -r parallelism and per-key --json
failure reporting (TIG-8731 AC items 3 and 4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@designcode designcode merged commit 7347f7e into main May 11, 2026
3 checks passed
@designcode designcode deleted the fix/mv-command branch May 11, 2026 11:38
designcode added a commit that referenced this pull request May 11, 2026
)

Picks up @tigrisdata/storage@3.5.2 which fixes
`403 SignatureDoesNotMatch` from `copy`, `move`, and `updateObject`
when the object key contains `/` or any character that requires
percent-encoding (space, `?`, `=`, etc.) and the request is signed
with access-key SigV4.

This unblocks the integration tests that have been failing on main
since #99 landed:

- `folder auto-detection > should auto-detect folder for cp/mv`
- `file to folder operations > *`
- `wildcard folder marker operations > *`
- `cp/mv command - additional branches > should *match wildcard*`

OAuth/session-token callers were unaffected because that auth path
skips SigV4 signing entirely, which is why this only surfaced once
the CI integration suite started exercising `copy`/`move` with
access keys after the cp/mv SDK swap in #99.

Verified locally with access-key auth against a real bucket:
- nested-key cp (`folder/file.txt`)
- special-char key cp (`folder/my file.txt`)
- same-bucket rename via `mv`

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants