Skip to content

Commit

Permalink
Blueprints: Support zipped assets without top-level folder (#491)
Browse files Browse the repository at this point in the history
Installing a plugin where the plugin files are directly at the top level
does not work. This PR assumes that, unless the zip file contains only a
single directory, the plugin files start at the top level.

Related: #427

Unit tests included.
Run `npm run dev` and confirm that adding ?gutenberg-pr=47739 to the URL
installs the PR.
  • Loading branch information
adamziel authored Jun 1, 2023
1 parent e94e919 commit 3856482
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/playground/blueprints/src/lib/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export class VFSResource extends Resource {

/** @inheritDoc */
get name() {
return this.resource.path;
return this.resource.path.split('/').pop() || '';
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ foreach ( ( glob( $plugin_path . '/*.php' ) ?: array() ) as $file ) {
echo 'NO_ENTRY_FILE';
`,
});
if (result.errors) throw new Error(result.errors);
if (result.text === 'NO_ENTRY_FILE') {
if (result.text.endsWith('NO_ENTRY_FILE')) {
throw new Error('Could not find plugin entry file.');
}
};
45 changes: 23 additions & 22 deletions packages/playground/blueprints/src/lib/steps/install-asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ export async function installAsset(
// Extract to temporary folder so we can find asset folder name

const zipFileName = zipFile.name;
const tmpFolder = `/tmp/assets`;
const assetNameGuess = zipFileName.replace(/\.zip$/, '');

const tmpUnzippedFilesPath = `/tmp/assets/${assetNameGuess}`;
const tmpZipPath = `/tmp/${zipFileName}`;

const removeTmpFolder = () =>
playground.rmdir(tmpFolder, {
playground.rmdir(tmpUnzippedFilesPath, {
recursive: true,
});

if (await playground.fileExists(tmpFolder)) {
if (await playground.fileExists(tmpUnzippedFilesPath)) {
await removeTmpFolder();
}

Expand All @@ -54,35 +56,34 @@ export async function installAsset(
try {
await unzip(playground, {
zipPath: tmpZipPath,
extractToPath: tmpFolder,
extractToPath: tmpUnzippedFilesPath,
});

// Find extracted asset folder name

const files = await playground.listFiles(tmpFolder);
// Find the path asset folder name
const files = await playground.listFiles(tmpUnzippedFilesPath, {
prependPath: true,
});

/**
* If the zip only contains a single entry that is directory,
* we assume that's the asset folder. Otherwise, the zip
* probably contains the plugin files without an intermediate folder.
*/
const zipHasRootFolder =
files.length === 1 && (await playground.isDir(files[0]));
let assetFolderName;
let tmpAssetPath = '';

for (const file of files) {
tmpAssetPath = `${tmpFolder}/${file}`;
if (await playground.isDir(tmpAssetPath)) {
assetFolderName = file;
break;
}
}

if (!assetFolderName) {
throw new Error(
`The zip file should contain a single folder with files inside, but the provided zip file (${zipFileName}) does not contain such a folder.`
);
if (zipHasRootFolder) {
tmpAssetPath = files[0];
assetFolderName = files[0].split('/').pop()!;
} else {
tmpAssetPath = tmpUnzippedFilesPath;
assetFolderName = assetNameGuess;
}

// Move asset folder to target path

const assetFolderPath = `${targetPath}/${assetFolderName}`;
await playground.mv(tmpAssetPath, assetFolderPath);

await cleanup();

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ describe('Blueprint step installPlugin', () => {

it('should install a plugin', async () => {
// Create test plugin

const pluginName = 'test-plugin';

php.mkdir(`/${pluginName}`);
Expand All @@ -28,7 +27,10 @@ describe('Blueprint step installPlugin', () => {
const zipFileName = `${pluginName}-0.0.1.zip`;

await php.run({
code: `<?php $zip = new ZipArchive(); $zip->open("${zipFileName}", ZIPARCHIVE::CREATE); $zip->addFile("/${pluginName}/index.php"); $zip->close();`,
code: `<?php $zip = new ZipArchive();
$zip->open("${zipFileName}", ZIPARCHIVE::CREATE);
$zip->addFile("/${pluginName}/index.php");
$zip->close();`,
});

php.rmdir(`/${pluginName}`);
Expand Down Expand Up @@ -63,4 +65,50 @@ describe('Blueprint step installPlugin', () => {

expect(php.fileExists(`${pluginsPath}/${pluginName}`)).toBe(true);
});

it('should install a plugin even when it is zipped directly without a root-level folder', async () => {
// Create test plugin
const pluginName = 'test-plugin';

php.writeFile('/index.php', `/**\n * Plugin Name: Test Plugin`);

// Note the package name is different from plugin folder name
const zipFileName = `${pluginName}-0.0.1.zip`;

await php.run({
code: `<?php $zip = new ZipArchive();
$zip->open("${zipFileName}", ZIPARCHIVE::CREATE);
$zip->addFile("/index.php");
$zip->close();`,
});

expect(php.fileExists(zipFileName)).toBe(true);

// Create plugins folder
const rootPath = await php.documentRoot;
const pluginsPath = `${rootPath}/wp-content/plugins`;

php.mkdir(pluginsPath);

await runBlueprintSteps(
compileBlueprint({
steps: [
{
step: 'installPlugin',
pluginZipFile: {
resource: 'vfs',
path: zipFileName,
},
options: {
activate: false,
},
},
],
}),
php
);

php.unlink(zipFileName);
expect(php.fileExists(`${pluginsPath}/${pluginName}-0.0.1`)).toBe(true);
});
});

0 comments on commit 3856482

Please sign in to comment.