Skip to content

Add selective sync to pull-reprint via reprint pull-files/pull-db - #3976

Merged
epeicher merged 29 commits into
trunkfrom
stu-1816-bring-selective-sync-and-interactive-file-selection-to-pull
Jul 17, 2026
Merged

Add selective sync to pull-reprint via reprint pull-files/pull-db#3976
epeicher merged 29 commits into
trunkfrom
stu-1816-bring-selective-sync-and-interactive-file-selection-to-pull

Conversation

@epeicher

@epeicher epeicher commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

AI (Claude Code) implemented the selector, the CLI flags, the split execution pipeline, the first-pull preservation of local wp-content, and the unit tests, and verified the behavior end-to-end against a live WP.com site (full first pull, partial first pull with local content preserved, --skip-database, scoped incremental re-pull, deferred media tail). I iterated multiple times over different approaches and verified the functionality manually.

Proposed Changes

Adds selective sync to the experimental pull-reprint command: users pick which wp-content folders, the media library, and the database to pull, and only that is fetched. Anything unselected keeps its local contents — including on a site's very first pull.

  • Choosing: an interactive wp-content folder tree + "Database" toggle (same on first and subsequent pulls), or the --only, --skip-database, --skip-uploads flags non-interactively. The choice is captured in a selection.json sidecar so a resumed pull reuses the exact same selection.
  • Executing: the single composite reprint pull is replaced by pull-filespull-db (skipped when the database is excluded) → flat-docrootapply-runtime, with --only scoping the file download. On a first pull the preflight-detected WordPress core roots are added to any partial selection so the site stays bootable, and the unselected local wp-content (and a kept database) is carried into the scratch before flattening.

The bulk of the diff handles WP Cloud's layout so a scoped pull still produces a working site — recreating the per-plugin symlinks a scoped listing omits, wiring a kept local database into the runtime, synthesizing the document-root wp-config.php that falls outside the selection, and keeping the selector's indexing side-effect free. See the inline comments for the specifics.

Testing Instructions

WordPress/reprint#334 (the fix for the --only incremental data-loss bug) is now merged to Reprint trunk but not yet in a tagged release, so the bundled reprint.phar (v0.9.0) is still affected. Until a release is cut, build the phar from Reprint trunk (composer build:phar in the reprint repo) and copy reprint.phar over apps/cli/dist/cli/wp-files/reprint/reprint.phar after npm run cli:build.

  1. Create a new site studio create or use an existing Studio site
  2. Run a full pull and select a valid remote site. For that run STUDIO_ENABLE_PULL_REPRINT=true node apps/cli/dist/cli/main.mjs pull-reprint --path <site> --url <remote> --verbose
  3. Verify that the selector is displayed and works as expected.
  4. Repeat the pull-reprint command on the same site, optionally, add a post or a media file to the remote site so you can test that it works as expected. Run the same command: STUDIO_ENABLE_PULL_REPRINT=true node apps/cli/dist/cli/main.mjs pull-reprint --path <site> --url <remote> --verbose
  5. Test different scenarios by checking/unchecking some folders and verify it works as expected.

Pre-merge Checklist

@epeicher
epeicher force-pushed the stu-1816-bring-selective-sync-and-interactive-file-selection-to-pull branch 3 times, most recently from 49be035 to b28b127 Compare July 2, 2026 11:22
@wpmobilebot

wpmobilebot commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing d58bc78 vs trunk

app-size

Metric trunk d58bc78 Diff Change
App Size (Mac) 1354.35 MB 1354.38 MB +0.03 MB ⚪ 0.0%

site-editor

Metric trunk d58bc78 Diff Change
load 743 ms 776 ms +33 ms ⚪ 0.0%

site-startup

Metric trunk d58bc78 Diff Change
siteCreation 6509 ms 6483 ms 26 ms ⚪ 0.0%
siteStartup 2392 ms 2388 ms 4 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@epeicher
epeicher force-pushed the stu-1816-bring-selective-sync-and-interactive-file-selection-to-pull branch from b28b127 to 24f60e0 Compare July 2, 2026 15:31
@epeicher epeicher changed the title Add selective sync menu and CLI flags to pull-reprint (captured, not yet applied) Jul 2, 2026
@epeicher epeicher changed the title Add selective sync menu and CLI flags to pull-reprint Jul 6, 2026
epeicher added 5 commits July 9, 2026 14:10
…tive-sync-and-interactive-file-selection-to-pull

# Conflicts:
#	apps/cli/commands/pull-reprint.ts
#	apps/cli/commands/tests/pull-reprint.test.ts
#	apps/cli/lib/pull/reprint-state.test.ts
#	apps/cli/lib/pull/reprint-state.ts
Comment thread apps/cli/commands/pull-reprint.ts Fixed
@epeicher
epeicher requested a review from a team July 9, 2026 23:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds selective sync capabilities to the experimental studio pull-reprint command by introducing a selection model (interactive tree or CLI flags) and splitting the Reprint execution into discrete steps that can be skipped/scoped while keeping the resulting site bootable.

Changes:

  • Introduces a Reprint-index-backed wp-content selector (plus database toggle) and CLI flags (--only, --skip-database, --skip-uploads) with a persisted selection.json for resumability.
  • Replaces the single composite reprint pull with a staged pipeline: pull-filespull-db (optional) → flat-docrootapply-runtime, including extra fixes for scoped pulls (wp-config synthesis, symlink restoration, skipped-files tail).
  • Adds first-pull preservation logic to carry unselected local wp-content (and optionally local DB/uploads) into the scratch before flattening, plus unit tests.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
apps/cli/lib/sync-selector.ts Exports buildRootTree for reuse by the Reprint selector UI.
apps/cli/lib/pull/reprint-state.ts Extends Reprint state accessors/mutators (core roots, ABSPATH, table prefix, skipped-files flag, sqlite runtime target, scratch reset helpers).
apps/cli/lib/pull/reprint-state.test.ts Adds unit coverage for the new Reprint state accessors/mutators.
apps/cli/lib/pull/reprint-selector.ts New selective-sync selector logic: build tree from Reprint remote index, map checked nodes/CLI flags to Reprint --only, track selected symlinks.
apps/cli/lib/pull/reprint-selector.test.ts Unit tests for selector parsing/mapping and token/path handling.
apps/cli/lib/pull/preserve-local-content.ts New first-pull preservation logic for unselected local wp-content entries.
apps/cli/lib/pull/preserve-local-content.test.ts Unit tests for local-content preservation behavior.
apps/cli/commands/tests/pull-reprint.test.ts Updates and expands command-level helper tests for the split pipeline, scoped pulls, and preservation behavior.
apps/cli/commands/pull-reprint.ts Wires selection into the command, splits Reprint pipeline execution, and threads selection/scoped behavior through download tail and runtime prep.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +713 to +716
if ( cliOnly.length > 0 ) {
const contentDir = getContentDirFromState( session.stateDirectory ) ?? '';
const mapped = mapCliOnlyToReprint( cliOnly, contentDir );
// Selected entries that are symlinks on the remote need their

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved as part of 6b1d257

Comment thread apps/cli/commands/pull-reprint.ts Outdated
Comment on lines 1030 to 1033
const contentDir = getContentDirFromState( metadata.stateDirectory );
const sqlitePath = contentDir
? `${ metadata.rawDirectory }${ contentDir }/database/.ht.sqlite`
: `${ metadata.sitePath }/wp-content/database/.ht.sqlite`;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved as part of b487105

Comment thread apps/cli/lib/pull/reprint-selector.ts Outdated
Comment on lines +106 to +117
let raw: string;
try {
raw = fs.readFileSync( remoteIndexPath, 'utf-8' );
} catch {
return { children: [], linkTargets: {} };
}

const entryByPath = new Map< string, { type?: string; target?: string } >();
const dirPrefixes = new Set< string >();
const contentEntries: string[] = [];

for ( const line of raw.split( '\n' ) ) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved as part of feb0f6f

@epeicher
epeicher requested a review from gcsecsey July 13, 2026 17:32
@gavande1

Copy link
Copy Markdown
Contributor

@epeicher, I tested this and I am still testing it, but I want to share some initial findings. I ran into a couple of issues.

So, I connected the remote site to a local Studio site and started testing selective sync.

First I installed a new theme on the remote site, then pulled the site into Studio. That worked as expected, and the theme showed up locally.

Next I installed the Code Snippets plugin on the remote site. I then ran a scoped pull for plugins and the database. After that pull, only Code Snippets was present locally. All of the other plugins were gone, including the managed ones like Akismet and Jetpack.

CleanShot 2026-07-14 at 18 38 39@2x

I then tried a full pull again to restore the complete site. It did not bring the missing plugins back. I repeated the full pull three times, and the site still did not restore fully on my local site.

~$ STUDIO_ENABLE_PULL_REPRINT=1 node apps/cli/dist/cli/main.mjs pull-reprint \
    --path ~/Studio/reprint-test \
    --url https://perfectmainframe00.wpcomstaging.com
 
 Site loaded
Updating "reprint-test" from https://perfectmainframe00.wpcomstaging.com/ (delta sync)

Technical directory: /Users/gavande/.studio/pulls/b4fb45d4-6323-4328-8627-535fa18fba01
Site directory: /Users/gavande/Studio/reprint-test

 Connected  WordPress 7.0.1, PHP 8.4.23
 Select what to pull from the remote site 51 items selected
 Site pulled
 Site "reprint-test" updated
 WordPress server already running

Site "reprint-test" is ready.

Site URL:  http://localhost:8887/studio-auto-login
WP Admin:  http://localhost:8887/studio-auto-login?redirect_to=http%3A%2F%2Flocalhost%3A8887%2Fwp-admin%2F


Site "reprint-test" pulled successfully.

Site URL:  http://localhost:8887/studio-auto-login
WP Admin:  http://localhost:8887/studio-auto-login?redirect_to=http%3A%2F%2Flocalhost%3A8887%2Fwp-admin%2F

Another nit: Please notice that, the auto login links are printed twice.

Note: I already confirmed that the reprint.phar in use was built from local Reprint trunk, not the packaged one.

@gcsecsey gcsecsey left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested selective pull with multiple local sites, selectively pulling either plugins, themes, or both, but I wasn't able to reproduce the above issue.

I could test full and partial pulls from multiple Atomic sites, and the changes makes sense to me. I think this is good to go, and we can fix the reported issue if we can successfully reproduce it later.

@epeicher

Copy link
Copy Markdown
Contributor Author

Thanks for testing and for the review @gavande1!

First I installed a new theme on the remote site, then pulled the site into Studio. That worked as expected, and the theme showed up locally.

Next I installed the Code Snippets plugin on the remote site. I then ran a scoped pull for plugins and the database. After that pull, only Code Snippets was present locally. All of the other plugins were gone, including the managed ones like Akismet and Jetpack.

I have tested this again, I have been able to partially pull the plugins and the Database, and the rest of the plugins are there. That issue seems to be related to this bug WordPress/reprint#334 so maybe the reprint.phar used didn't contain that fix? Could you please double-check with the latest reprint.phar?

Incremental pull Plugins after incremental pull
CleanShot 2026-07-15 at 09 14 23@2x CleanShot 2026-07-15 at 09 15 15@2x

Another nit: Please notice that, the auto login links are printed twice.

That's existing Reprint behavior, it seems to be displaying the links after the two steps, feel free to create a follow-up task as this is not related with these changes.

@gavande1

Copy link
Copy Markdown
Contributor

Thanks, @epeicher, for taking a look. I tested it on a fresh site and it worked well, but I hit the same issue on trunk, and it is reproducible there too. It looks like something needs to be fixed in Reprint.

I connected a WP Cloud site and tested selective sync. Most of it worked, but WooCommerce did not come through.

Journey:

  • I connected the remote site to a local Studio site.
  • I ran a full pull. The site came down and most plugins loaded correctly, including Jetpack, Akismet, and Gutenberg.
  • I opened the plugins list and noticed WooCommerce was missing locally, even though it is installed and active on the remote.
  • I looked at the pulled files to understand why. The working plugins are symlinked to a version that was downloaded, so they resolve. WooCommerce is symlinked to the accelerated-frequency release channel, and that channel points to a woocommerce-accelerated-frequency package that was never downloaded. Only woocommerce/10.9.4 was pulled, so the WooCommerce link dead-ends and the plugin does not load.

Expected: WooCommerce should be present and active locally, matching the remote.

@gavande1 gavande1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @epeicher for working on this. The selective sync is working as expected. I extensively tested this from different prospective and it worked really well. LGTM 👍

@epeicher

Copy link
Copy Markdown
Contributor Author

Thanks @gavande1 and @gcsecsey for your review and your testing ❤️

I connected a WP Cloud site and tested selective sync. Most of it worked, but WooCommerce did not come through.

Thanks for reporting and the detailed context, I have created a follow-up task for that STU-2056

@epeicher
epeicher enabled auto-merge (squash) July 17, 2026 15:26
@epeicher
epeicher merged commit 7f63118 into trunk Jul 17, 2026
14 checks passed
@epeicher
epeicher deleted the stu-1816-bring-selective-sync-and-interactive-file-selection-to-pull branch July 17, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

6 participants