diff --git a/packages/playground/blueprints/src/lib/resources.ts b/packages/playground/blueprints/src/lib/resources.ts index 93065089cc..d08d0485f4 100644 --- a/packages/playground/blueprints/src/lib/resources.ts +++ b/packages/playground/blueprints/src/lib/resources.ts @@ -164,7 +164,7 @@ export class VFSResource extends Resource { /** @inheritDoc */ get name() { - return this.resource.path; + return this.resource.path.split('/').pop() || ''; } } diff --git a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts index 72efa36a8a..3f46081122 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts @@ -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.'); } }; diff --git a/packages/playground/blueprints/src/lib/steps/install-asset.ts b/packages/playground/blueprints/src/lib/steps/install-asset.ts index ed613d5e8f..40649457ff 100644 --- a/packages/playground/blueprints/src/lib/steps/install-asset.ts +++ b/packages/playground/blueprints/src/lib/steps/install-asset.ts @@ -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(); } @@ -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 { diff --git a/packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts b/packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts index 15b48f7fae..85a5397228 100644 --- a/packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts +++ b/packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts @@ -15,7 +15,6 @@ describe('Blueprint step installPlugin', () => { it('should install a plugin', async () => { // Create test plugin - const pluginName = 'test-plugin'; php.mkdir(`/${pluginName}`); @@ -28,7 +27,10 @@ describe('Blueprint step installPlugin', () => { const zipFileName = `${pluginName}-0.0.1.zip`; await php.run({ - code: `open("${zipFileName}", ZIPARCHIVE::CREATE); $zip->addFile("/${pluginName}/index.php"); $zip->close();`, + code: `open("${zipFileName}", ZIPARCHIVE::CREATE); + $zip->addFile("/${pluginName}/index.php"); + $zip->close();`, }); php.rmdir(`/${pluginName}`); @@ -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: `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); + }); });