Skip to content

Commit 4ebf208

Browse files
committed
Blueprints: Support zipped assets without top-level folder
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.
1 parent 47c2832 commit 4ebf208

File tree

4 files changed

+75
-27
lines changed

4 files changed

+75
-27
lines changed

packages/playground/blueprints/src/lib/resources.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class VFSResource extends Resource {
164164

165165
/** @inheritDoc */
166166
get name() {
167-
return this.resource.path;
167+
return this.resource.path.split('/').pop() || '';
168168
}
169169
}
170170

packages/playground/blueprints/src/lib/steps/activate-plugin.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ foreach ( ( glob( $plugin_path . '/*.php' ) ?: array() ) as $file ) {
5151
echo 'NO_ENTRY_FILE';
5252
`,
5353
});
54-
if (result.errors) throw new Error(result.errors);
55-
if (result.text === 'NO_ENTRY_FILE') {
54+
if (result.text.endsWith('NO_ENTRY_FILE')) {
5655
throw new Error('Could not find plugin entry file.');
5756
}
5857
};

packages/playground/blueprints/src/lib/steps/install-asset.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ export async function installAsset(
3131
// Extract to temporary folder so we can find asset folder name
3232

3333
const zipFileName = zipFile.name;
34-
const tmpFolder = `/tmp/assets`;
34+
const assetNameGuess = zipFileName.replace(/\.zip$/, '');
35+
36+
const tmpUnzippedFilesPath = `/tmp/assets/${assetNameGuess}`;
3537
const tmpZipPath = `/tmp/${zipFileName}`;
3638

3739
const removeTmpFolder = () =>
38-
playground.rmdir(tmpFolder, {
40+
playground.rmdir(tmpUnzippedFilesPath, {
3941
recursive: true,
4042
});
4143

42-
if (await playground.fileExists(tmpFolder)) {
44+
if (await playground.fileExists(tmpUnzippedFilesPath)) {
4345
await removeTmpFolder();
4446
}
4547

@@ -54,35 +56,34 @@ export async function installAsset(
5456
try {
5557
await unzip(playground, {
5658
zipPath: tmpZipPath,
57-
extractToPath: tmpFolder,
59+
extractToPath: tmpUnzippedFilesPath,
5860
});
5961

60-
// Find extracted asset folder name
61-
62-
const files = await playground.listFiles(tmpFolder);
62+
// Find the path asset folder name
63+
const files = await playground.listFiles(tmpUnzippedFilesPath, {
64+
prependPath: true,
65+
});
6366

67+
/**
68+
* If the zip only contains a single entry that is directory,
69+
* we assume that's the asset folder. Otherwise, the zip
70+
* probably contains the plugin files without an intermediate folder.
71+
*/
72+
const zipHasRootFolder =
73+
files.length === 1 && (await playground.isDir(files[0]));
6474
let assetFolderName;
6575
let tmpAssetPath = '';
66-
67-
for (const file of files) {
68-
tmpAssetPath = `${tmpFolder}/${file}`;
69-
if (await playground.isDir(tmpAssetPath)) {
70-
assetFolderName = file;
71-
break;
72-
}
73-
}
74-
75-
if (!assetFolderName) {
76-
throw new Error(
77-
`The zip file should contain a single folder with files inside, but the provided zip file (${zipFileName}) does not contain such a folder.`
78-
);
76+
if (zipHasRootFolder) {
77+
tmpAssetPath = files[0];
78+
assetFolderName = files[0].split('/').pop()!;
79+
} else {
80+
tmpAssetPath = tmpUnzippedFilesPath;
81+
assetFolderName = assetNameGuess;
7982
}
8083

8184
// Move asset folder to target path
82-
8385
const assetFolderPath = `${targetPath}/${assetFolderName}`;
8486
await playground.mv(tmpAssetPath, assetFolderPath);
85-
8687
await cleanup();
8788

8889
return {

packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ describe('Blueprint step installPlugin', () => {
1515

1616
it('should install a plugin', async () => {
1717
// Create test plugin
18-
1918
const pluginName = 'test-plugin';
2019

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

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

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

6466
expect(php.fileExists(`${pluginsPath}/${pluginName}`)).toBe(true);
6567
});
68+
69+
it('should install a plugin even when it is zipped directly without a root-level folder', async () => {
70+
// Create test plugin
71+
const pluginName = 'test-plugin';
72+
73+
php.writeFile('/index.php', `/**\n * Plugin Name: Test Plugin`);
74+
75+
// Note the package name is different from plugin folder name
76+
const zipFileName = `${pluginName}-0.0.1.zip`;
77+
78+
await php.run({
79+
code: `<?php $zip = new ZipArchive();
80+
$zip->open("${zipFileName}", ZIPARCHIVE::CREATE);
81+
$zip->addFile("/index.php");
82+
$zip->close();`,
83+
});
84+
85+
expect(php.fileExists(zipFileName)).toBe(true);
86+
87+
// Create plugins folder
88+
const rootPath = await php.documentRoot;
89+
const pluginsPath = `${rootPath}/wp-content/plugins`;
90+
91+
php.mkdir(pluginsPath);
92+
93+
await runBlueprintSteps(
94+
compileBlueprint({
95+
steps: [
96+
{
97+
step: 'installPlugin',
98+
pluginZipFile: {
99+
resource: 'vfs',
100+
path: zipFileName,
101+
},
102+
options: {
103+
activate: false,
104+
},
105+
},
106+
],
107+
}),
108+
php
109+
);
110+
111+
php.unlink(zipFileName);
112+
expect(php.fileExists(`${pluginsPath}/${pluginName}-0.0.1`)).toBe(true);
113+
});
66114
});

0 commit comments

Comments
 (0)