@@ -670,9 +670,10 @@ export async function runCommand(
670670 }
671671
672672 if ( ! hasPullCompletedStage ( studioMetadata , 'completed' ) ) {
673- // Fetch the deferred media/uploads. The selector's media choice is not
674- // applied yet (see runFullPull) — wired into pull-files in the follow-up.
675- if ( hasSkippedFiles ( studioMetadata . stateDirectory ) ) {
673+ // Fetch the deferred media/uploads unless the user excluded the media
674+ // library. Core, plugins, and themes already came down in the
675+ // essential-files pass; this second pass is just the media library.
676+ if ( ! studioMetadata . skipUploads && hasSkippedFiles ( studioMetadata . stateDirectory ) ) {
676677 await downloadSkippedFiles (
677678 getSiteRuntime ( site ) ,
678679 studioMetadata ,
@@ -1017,26 +1018,27 @@ function readPullMetadata( metadataPath: string ): PullSessionMetadata | null {
10171018}
10181019
10191020/**
1020- * Run reprint's composite `pull` command: the whole site-clone
1021- * pipeline (preflight → files-pull → db-pull → db-apply →
1022- * flat-docroot → apply-runtime) in a single child process, with
1023- * reprint owning the stage ordering and, when the prior pull already
1024- * completed, resetting its own sub-command state for a delta re-pull
1025- * via prepare_repull().
1021+ * Run the site-clone pipeline as three high-level reprint commands so the
1022+ * selective-sync choice maps cleanly onto them:
10261023 *
1027- * The SQLite target geometry:
1028- * - If preflight exposed the remote ` wp-content` (contentDir set),
1029- * the database lands under `rawDirectory + contentDir`, an
1030- * already-mounted host path that flat-docroot later symlinks into
1031- * the flattened site.
1032- * - Otherwise it falls back to `sitePath/wp-content` .
1024+ * 1. `pull-files` — files + flattened layout (`--only` restricts to the
1025+ * chosen wp-content folders).
1026+ * 2. `pull-db` — database download + import. **Skipped entirely** when the
1027+ * user excluded the database.
1028+ * 3. `apply-runtime` — server config, run last so it picks up the DB
1029+ * credentials `pull-db` wrote to state .
10331030 *
1034- * The flattened site (`--flatten-to`) and runtime output
1035- * (`--output-dir`) directories are mounted up front so the single
1036- * fork can write them onto the host filesystem. `ensurePort` must
1037- * run first so `--new-site-url` points at the local server.
1031+ * DRAFT — `pull-files`/`pull-db` are high-level commands Reprint is adding to
1032+ * hide the low-level orchestration (files-index/files-pull/flat-docroot and
1033+ * db-pull/db-apply). Names, arguments, and which command owns flatten/runtime
1034+ * are **guesses** (marked `TODO(reprint pull-files/pull-db)`); reconcile once
1035+ * the real interface lands. Optimized for simplicity and host-agnostic reuse:
1036+ * Studio just picks files vs db and passes `--only`; the commands own the rest.
10381037 *
1039- * Advances the pull stage to 'pulled'.
1038+ * SQLite target geometry and the `--flatten-to`/`--output-dir` mounts carry
1039+ * over from the previous composite-`pull` call. Advances the stage to 'pulled';
1040+ * the three commands are individually resumable/idempotent, so a crash between
1041+ * them safely re-runs the lot on the next pass.
10401042 */
10411043export async function runFullPull (
10421044 runtime : SiteRuntime ,
@@ -1050,41 +1052,67 @@ export async function runFullPull(
10501052 ? `${ metadata . rawDirectory } ${ contentDir } /database/.ht.sqlite`
10511053 : `${ metadata . sitePath } /wp-content/database/.ht.sqlite` ;
10521054 const reprintRuntime = runtime === SITE_RUNTIME_NATIVE_PHP ? 'nginx-fpm' : 'playground-cli' ;
1055+ const onlyArgs = ( metadata . fileOnlyPaths ?? [ ] ) . map ( ( onlyPath ) => `--only=${ onlyPath } ` ) ;
1056+ const mounts = [
1057+ { hostPath : metadata . sitePath , vfsPath : metadata . sitePath } ,
1058+ { hostPath : metadata . runtimeDirectory , vfsPath : metadata . runtimeDirectory } ,
1059+ ] ;
1060+
1061+ const runStep = ( progressLabel : string , args : string [ ] ) =>
1062+ runReprintCommandUntilComplete (
1063+ metadata . stateDirectory ,
1064+ metadata . rawDirectory ,
1065+ args ,
1066+ ( progress ) => logger . reportProgress ( progress ) ,
1067+ { progressLabel, mounts, verboseCommands : verbose , runtime }
1068+ ) ;
10531069
10541070 logger . reportStart ( LoggerAction . DOWNLOAD_FILES , __ ( 'Pulling site…' ) ) ;
1055- await runReprintCommandUntilComplete (
1056- metadata . stateDirectory ,
1057- metadata . rawDirectory ,
1058- [
1059- 'pull' ,
1071+
1072+ // 1. Files (+ flattened layout). `--only` restricts to the selected folders.
1073+ // TODO(reprint pull-files): confirm command name; whether it owns flat-docroot;
1074+ // and how it expresses "defer uploads" (assumed --filter=essential-files +
1075+ // the deferred files-sync pass below).
1076+ await runStep ( __ ( 'Pulling files' ) , [
1077+ 'pull-files' ,
1078+ apiUrl ,
1079+ `--secret=${ secret } ` ,
1080+ '--filter=essential-files' ,
1081+ ...onlyArgs ,
1082+ `--flatten-to=${ metadata . sitePath } ` ,
1083+ '--no-adaptive' ,
1084+ `--state-dir=${ metadata . stateDirectory } ` ,
1085+ `--fs-root=${ metadata . rawDirectory } ` ,
1086+ ] ) ;
1087+
1088+ // 2. Database — only when selected. Skipping it leaves the local DB untouched
1089+ // on a re-pull (apply-runtime keeps the credentials already in state).
1090+ // TODO(reprint pull-db): confirm command name + target/rewrite args.
1091+ if ( ! metadata . skipDatabase ) {
1092+ await runStep ( __ ( 'Pulling database' ) , [
1093+ 'pull-db' ,
10601094 apiUrl ,
10611095 `--secret=${ secret } ` ,
1062- '--filter=essential-files' ,
10631096 '--target-engine=sqlite' ,
10641097 `--target-sqlite-path=${ sqlitePath } ` ,
10651098 `--new-site-url=${ metadata . localUrl ! } ` ,
1066- `--flatten-to=${ metadata . sitePath } ` ,
1067- `--runtime=${ reprintRuntime } ` ,
1068- '--start-runtime=none' ,
1069- `--output-dir=${ metadata . runtimeDirectory } ` ,
10701099 '--no-adaptive' ,
10711100 `--state-dir=${ metadata . stateDirectory } ` ,
10721101 `--fs-root=${ metadata . rawDirectory } ` ,
1073- // NOTE: the interactive selector / `--only` / `--skip-*` choices are
1074- // captured in metadata but NOT applied yet — this is a full pull. The
1075- // selection is wired into `pull-files`/`pull-db` in the follow-up PR.
1076- ] ,
1077- ( progress ) => logger . reportProgress ( progress ) ,
1078- {
1079- progressLabel : __ ( 'Pulling site' ) ,
1080- mounts : [
1081- { hostPath : metadata . sitePath , vfsPath : metadata . sitePath } ,
1082- { hostPath : metadata . runtimeDirectory , vfsPath : metadata . runtimeDirectory } ,
1083- ] ,
1084- verboseCommands : verbose ,
1085- runtime,
1086- }
1087- ) ;
1102+ ] ) ;
1103+ }
1104+
1105+ // 3. Runtime config — last, so it embeds the DB credentials pull-db wrote.
1106+ await runStep ( __ ( 'Preparing runtime' ) , [
1107+ 'apply-runtime' ,
1108+ '-' ,
1109+ `--runtime=${ reprintRuntime } ` ,
1110+ `--output-dir=${ metadata . runtimeDirectory } ` ,
1111+ `--flat-document-root=${ metadata . sitePath } ` ,
1112+ '--no-adaptive' ,
1113+ `--state-dir=${ metadata . stateDirectory } ` ,
1114+ ] ) ;
1115+
10881116 logger . reportSuccess ( __ ( 'Site pulled' ) ) ;
10891117 recordCompletedStage ( metadata , 'pulled' ) ;
10901118}
@@ -1440,7 +1468,7 @@ export function getReprintApiUrlForSite( siteUrl: string ): string {
14401468}
14411469
14421470function buildFilesSyncArgs (
1443- metadata : Pick < PullSessionMetadata , 'stateDirectory' | 'rawDirectory' > ,
1471+ metadata : Pick < PullSessionMetadata , 'stateDirectory' | 'rawDirectory' | 'fileOnlyPaths' > ,
14441472 apiUrl : string ,
14451473 secret : string ,
14461474 extraArgs : string [ ] = [ ]
@@ -1450,6 +1478,10 @@ function buildFilesSyncArgs(
14501478 apiUrl ,
14511479 `--secret=${ secret } ` ,
14521480 ...extraArgs ,
1481+ // Carry the same `--only` set as pull-files so the deferred pass stays in
1482+ // scope (files-sync's index is a union keyed by a fingerprint of the
1483+ // prefixes and refuses to resume with a different `--only`).
1484+ ...( metadata . fileOnlyPaths ?? [ ] ) . map ( ( onlyPath ) => `--only=${ onlyPath } ` ) ,
14531485 // Per-batch ceiling — one sub-process yields after 30 s and the
14541486 // client reconnects to continue. Not a total-time budget; a slow
14551487 // or high-latency sync just makes more round-trips. Kept well
0 commit comments