Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions apps/cli/commands/pull-reprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ export async function runCommand(
verbose,
selection
);
// The tail landed in the raw directory after flat-docroot ran, so
// link the files that just arrived into the flattened site.
await relinkFlattenedSite( getSiteRuntime( site ), studioMetadata, verbose );
}

// The pull is done: drop the selection sidecar so the next pull asks
Expand Down Expand Up @@ -1138,6 +1141,15 @@ export async function runFullPull(
'flat-docroot',
'-',
`--flatten-to=${ metadata.sitePath }`,
// `--force` replaces the blank install `studio create` produced, but on
// its own it would also delete everything the user had added to
// wp-content. `--preserve-local-content` merges wp-content instead:
// the remote wins on conflicts, local-only plugins/themes/uploads stay.
//
// It is passed on *every* pull, not just the first: once the first pull
// merges, wp-content is a real directory, and a delta re-pull without
// the flag would fail trying to symlink over it.
'--preserve-local-content',
...( force ? [ '--force' ] : [] ),
`--state-dir=${ metadata.stateDirectory }`,
`--fs-root=${ metadata.rawDirectory }`,
Expand Down Expand Up @@ -1217,6 +1229,45 @@ export async function downloadSkippedFiles(
logger.reportSuccess( __( 'Remaining files downloaded' ) );
}

/**
* Re-link the flattened site after the deferred file tail.
*
* `downloadSkippedFiles` lands media in the raw directory *after*
* `flat-docroot` already ran. The flattened site links the remote's entries
* individually rather than aliasing the whole `wp-content`, so anything that
* arrives late has no link yet and would stay invisible until the next pull.
*
* Re-running the flatten is idempotent and cheap: it refreshes the links that
* are already correct, adds links for the newly-arrived files, and prunes the
* ones whose target the remote dropped. `--force` is deliberately omitted —
* the site is already assembled, so nothing should be replaced here.
*/
export async function relinkFlattenedSite(
runtime: SiteRuntime,
metadata: PullSession,
verbose: boolean
): Promise< void > {
await runReprintCommandUntilComplete(
metadata.stateDirectory,
metadata.rawDirectory,
[
'flat-docroot',
'-',
`--flatten-to=${ metadata.sitePath }`,
'--preserve-local-content',
`--state-dir=${ metadata.stateDirectory }`,
`--fs-root=${ metadata.rawDirectory }`,
],
( progress ) => logger.reportProgress( progress ),
{
progressLabel: __( 'Linking new files' ),
mounts: [ { hostPath: metadata.sitePath, vfsPath: metadata.sitePath } ],
verboseCommands: verbose,
runtime,
}
);
}

export function normalizeSiteUrl( url: string ): string {
const trimmedUrl = url.trim();
const normalized = new URL(
Expand Down
6 changes: 6 additions & 0 deletions apps/cli/commands/tests/pull-reprint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,13 @@ describe( 'CLI: studio pull-reprint single pull phase', () => {
] );
// Local flatten: `-` URL placeholder; --force only on a first pull
// (this call passed force=true) to overwrite the blank install.
// --preserve-local-content goes on every pull so wp-content is merged
// rather than replaced and locally installed files survive.
expect( flattenArgs ).toEqual( [
'flat-docroot',
'-',
`--flatten-to=${ sitePath }`,
'--preserve-local-content',
'--force',
`--state-dir=${ stateDirectory }`,
`--fs-root=${ rawDirectory }`,
Expand Down Expand Up @@ -464,6 +467,9 @@ describe( 'CLI: studio pull-reprint single pull phase', () => {
expect( commands ).toEqual( [ 'pull-files', 'flat-docroot', 'apply-runtime' ] );
const flattenArgs = reprint.mock.calls[ 1 ][ 2 ] as string[];
expect( flattenArgs ).not.toContain( '--force' );
// Once merged, wp-content is a real directory; a delta pull without
// the flag would fail trying to symlink over it.
expect( flattenArgs ).toContain( '--preserve-local-content' );

fs.rmSync( technicalSiteDirectory, { recursive: true, force: true } );
} );
Expand Down
Loading