Skip to content
3 changes: 2 additions & 1 deletion RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Unreleased
==========
* Miscellaneous security improvements #1411
* Follow symlinks when creating preview sites #1447
* Included symlinked directories and files when creating preview sites #1447
* Included symlinked directories and files when exporting and pushing sites #1448

1.5.2
=====
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ export const ACCEPTED_IMPORT_FILE_TYPES = [ '.zip', '.gz', '.gzip', '.tar', '.ta
export const ARCHIVER_OPTIONS = {
zip: {
zlib: { level: 9 },
followSymlinks: true,
},
tar: {
gzip: true,
gzipOptions: { level: 9 },
followSymlinks: true,
},
};

Expand Down
23 changes: 18 additions & 5 deletions src/lib/import-export/export/exporters/default-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,24 @@ export class DefaultExporter extends EventEmitter implements Exporter {
).filter( ( category ) => this.options.includes[ category ] );
this.emit( ExportEvents.WP_CONTENT_EXPORT_START );
for ( const category of categories ) {
for ( const file of this.backup.wpContent[ category ] ) {
const relativePath = path.relative( this.options.site.path, file );
this.archive.file( file, { name: relativePath } );
this.emit( ExportEvents.WP_CONTENT_EXPORT_PROGRESS, { file: relativePath } );
}
const folderName = category === 'muPlugins' ? 'mu-plugins' : category;
const absolutePath = path.join( this.options.site.path, 'wp-content', folderName );
const archivePath = path.relative( this.options.site.path, absolutePath );
this.archive.directory( absolutePath, archivePath, ( entry ) => {
const fullArchivePath = path.join( archivePath, entry.name );
const isExcluded = this.pathsToExclude.some( ( pathToExclude ) =>
fullArchivePath.startsWith( path.normalize( pathToExclude ) )
);
if (
isExcluded ||
entry.name.includes( '.git' ) ||
entry.name.includes( 'node_modules' )
) {
return false;
}
return entry;
} );
this.emit( ExportEvents.WP_CONTENT_EXPORT_PROGRESS, { directory: absolutePath } );
}
this.emit( ExportEvents.WP_CONTENT_EXPORT_COMPLETE, {
uploads: this.backup.wpContent.uploads.length,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,18 @@ platformTestSuite( 'DefaultExporter', ( { normalize } ) => {
it( 'should create a tar.gz archive', async () => {
await exporter.export();

expect( archiver ).toHaveBeenCalledWith( 'tar', { gzip: true, gzipOptions: { level: 9 } } );
expect( archiver ).toHaveBeenCalledWith( 'tar', {
followSymlinks: true,
gzip: true,
gzipOptions: { level: 9 },
} );
} );

it( 'should create a zip archive when the backup file ends with .zip', async () => {
mockOptions.backupFile = '/path/to/backup.zip';
await exporter.export();

expect( archiver ).toHaveBeenCalledWith( 'zip', { zlib: { level: 9 } } );
expect( archiver ).toHaveBeenCalledWith( 'zip', { followSymlinks: true, zlib: { level: 9 } } );
} );

it( 'should add wp-config.php to the archive', async () => {
Expand Down Expand Up @@ -262,7 +266,7 @@ platformTestSuite( 'DefaultExporter', ( { normalize } ) => {
);
} );

it( 'should add wp-content files to the archive', async () => {
it( 'should add wp-content directories to the archive', async () => {
const options = {
...mockOptions,
includes: {
Expand All @@ -283,25 +287,29 @@ platformTestSuite( 'DefaultExporter', ( { normalize } ) => {
normalize( '/path/to/site/wp-config.php' ),
{ name: 'wp-config.php' }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
expect( mockArchiver.directory ).toHaveBeenNthCalledWith(
1,
normalize( '/path/to/site/wp-content/uploads' ),
normalize( 'wp-content/uploads' ),
expect.any( Function )
);
expect( mockArchiver.directory ).toHaveBeenNthCalledWith(
2,
normalize( '/path/to/site/wp-content/uploads/file1.jpg' ),
{ name: normalize( 'wp-content/uploads/file1.jpg' ) }
normalize( '/path/to/site/wp-content/plugins' ),
normalize( 'wp-content/plugins' ),
expect.any( Function )
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
expect( mockArchiver.directory ).toHaveBeenNthCalledWith(
3,
normalize( '/path/to/site/wp-content/plugins/plugin1/plugin1.php' ),
{ name: normalize( 'wp-content/plugins/plugin1/plugin1.php' ) }
normalize( '/path/to/site/wp-content/themes' ),
normalize( 'wp-content/themes' ),
expect.any( Function )
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
expect( mockArchiver.directory ).toHaveBeenNthCalledWith(
4,
normalize( '/path/to/site/wp-content/themes/theme1/index.php' ),
{ name: normalize( 'wp-content/themes/theme1/index.php' ) }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
5,
normalize( '/path/to/site/wp-content/fonts/custom-font.woff2' ),
{ name: normalize( 'wp-content/fonts/custom-font.woff2' ) }
normalize( '/path/to/site/wp-content/fonts' ),
normalize( 'wp-content/fonts' ),
expect.any( Function )
);
} );

Expand All @@ -326,19 +334,11 @@ platformTestSuite( 'DefaultExporter', ( { normalize } ) => {
normalize( '/path/to/site/wp-config.php' ),
{ name: 'wp-config.php' }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
2,
normalize( '/path/to/site/wp-content/mu-plugins/custom-mu-plugin.php' ),
{ name: normalize( 'wp-content/mu-plugins/custom-mu-plugin.php' ) }
);

expect( mockArchiver.file ).not.toHaveBeenCalledWith(
normalize( '/path/to/site/wp-content/mu-plugins/sqlite-database-integration/load.php' ),
{ name: normalize( 'wp-content/mu-plugins/sqlite-database-integration/load.php' ) }
);
expect( mockArchiver.file ).not.toHaveBeenCalledWith(
normalize( '/path/to/site/wp-content/mu-plugins/0-allowed-redirect-hosts.php' ),
{ name: normalize( 'wp-content/mu-plugins/0-allowed-redirect-hosts.php' ) }
expect( mockArchiver.directory ).toHaveBeenNthCalledWith(
1,
normalize( '/path/to/site/wp-content/mu-plugins' ),
normalize( 'wp-content/mu-plugins' ),
expect.any( Function )
);
} );

Expand Down Expand Up @@ -486,10 +486,11 @@ platformTestSuite( 'DefaultExporter', ( { normalize } ) => {
normalize( '/path/to/site/wp-config.php' ),
{ name: 'wp-config.php' }
);
expect( mockArchiver.file ).toHaveBeenNthCalledWith(
2,
normalize( '/path/to/site/wp-content/fonts/custom-font.woff2' ),
{ name: normalize( 'wp-content/fonts/custom-font.woff2' ) }
expect( mockArchiver.directory ).toHaveBeenNthCalledWith(
1,
normalize( '/path/to/site/wp-content/fonts' ),
normalize( 'wp-content/fonts' ),
expect.any( Function )
);
} );
} );
Loading