-
Notifications
You must be signed in to change notification settings - Fork 268
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Blueprints: Add ifAlreadyInstalled to installPlugin and installTheme …
…steps (#1244) Adds an `ifAlreadyInstalled?: 'overwrite' | 'skip' | 'error'` option to installPlugin and installTheme steps. It defaults to `overwrite`. Consider the following Blueprint: ```json { "preferredVersions": { "php": "latest", "wp": "6.4" }, "steps": [ { "step": "installTheme", "themeZipFile": { "resource": "wordpress.org/themes", "slug": "twentytwentyfour" } } ] } ``` Before this PR, it would result in an error. After this PR, the installation just works. If the Blueprint author explicitly wants the installation to fail, they can specify the `ifAlreadyInstalled` option: ```json { "steps": [ { "step": "installTheme", "themeZipFile": { "resource": "wordpress.org/themes", "slug": "twentytwentyfour" }, "ifAlreadyInstalled": "skip" // or "error" } ] } ``` ## Motivation Installing a plugin or theme over a currently installed one is a common gotcha. Currently it results in an error and blocks the Blueprint execution. This behavior is, however, often undesirable as it prevents having a single Blueprint that installs a twentytwentyfour theme on different versions of WordPress. An addition of the `ifAlreadyInstalled` option puts the Blueprint author in control and provides a sensible default behavior where the installation will "just work" by replacing the already installed version of the plugin or theme. Closes #1157 Related to WordPress/blueprints#19 ## Testing instructions Confirm the unit tests pass cc @bgrgicak @brandonpayton
- Loading branch information
Showing
6 changed files
with
270 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
220 changes: 133 additions & 87 deletions
220
packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,159 @@ | ||
import { NodePHP } from '@php-wasm/node'; | ||
import { compileBlueprint, runBlueprintSteps } from '../compile'; | ||
import { RecommendedPHPVersion } from '@wp-playground/wordpress'; | ||
import { installPlugin } from './install-plugin'; | ||
import { phpVar } from '@php-wasm/util'; | ||
|
||
async function zipFiles( | ||
php: NodePHP, | ||
fileName: string, | ||
files: Record<string, string> | ||
) { | ||
const zipFileName = 'test.zip'; | ||
const zipFilePath = `/${zipFileName}`; | ||
|
||
await php.run({ | ||
code: `<?php $zip = new ZipArchive(); | ||
$zip->open("${zipFileName}", ZIPARCHIVE::CREATE); | ||
$files = ${phpVar(files)}; | ||
foreach($files as $path => $content) { | ||
$zip->addFromString($path, $content); | ||
} | ||
$zip->close();`, | ||
}); | ||
|
||
describe('Blueprint step installPlugin', () => { | ||
let php: NodePHP; | ||
beforeEach(async () => { | ||
php = await NodePHP.load(RecommendedPHPVersion, { | ||
const zip = await php.readFileAsBuffer(zipFilePath); | ||
php.unlink(zipFilePath); | ||
return new File([zip], fileName); | ||
} | ||
|
||
describe('Blueprint step installPlugin – without a root-level folder', () => { | ||
it('should install a plugin even when it is zipped directly without a root-level folder', async () => { | ||
const php = await NodePHP.load(RecommendedPHPVersion, { | ||
requestHandler: { | ||
documentRoot: '/wordpress', | ||
}, | ||
}); | ||
}); | ||
|
||
it('should install a plugin', async () => { | ||
// Create test plugin | ||
const pluginName = 'test-plugin'; | ||
|
||
php.mkdir(`/${pluginName}`); | ||
php.writeFile( | ||
`/${pluginName}/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("/${pluginName}/index.php"); | ||
$zip->close();`, | ||
}); | ||
|
||
php.rmdir(`/${pluginName}`); | ||
|
||
expect(php.fileExists(zipFileName)).toBe(true); | ||
|
||
// Create plugins folder | ||
const rootPath = await php.documentRoot; | ||
const rootPath = 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 | ||
); | ||
// Create test plugin | ||
const pluginName = 'test-plugin'; | ||
|
||
php.unlink(zipFileName); | ||
await installPlugin(php, { | ||
pluginZipFile: await zipFiles( | ||
php, | ||
// Note the ZIP filename is different from plugin folder name | ||
`${pluginName}-0.0.1.zip`, | ||
{ | ||
'index.php': `/**\n * Plugin Name: Test Plugin`, | ||
} | ||
), | ||
ifAlreadyInstalled: 'overwrite', | ||
options: { | ||
activate: false, | ||
}, | ||
}); | ||
|
||
expect(php.fileExists(`${pluginsPath}/${pluginName}`)).toBe(true); | ||
expect(php.fileExists(`${pluginsPath}/${pluginName}-0.0.1`)).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`); | ||
describe('Blueprint step installPlugin', () => { | ||
let php: NodePHP; | ||
// Create plugins folder | ||
let rootPath = ''; | ||
let installedPluginPath = ''; | ||
const pluginName = 'test-plugin'; | ||
const zipFileName = `${pluginName}-0.0.1.zip`; | ||
beforeEach(async () => { | ||
php = await NodePHP.load(RecommendedPHPVersion, { | ||
requestHandler: { | ||
documentRoot: '/wordpress', | ||
}, | ||
}); | ||
rootPath = php.documentRoot; | ||
php.mkdir(`${rootPath}/wp-content/plugins`); | ||
installedPluginPath = `${rootPath}/wp-content/plugins/${pluginName}`; | ||
}); | ||
|
||
// Note the package name is different from plugin folder name | ||
const zipFileName = `${pluginName}-0.0.1.zip`; | ||
afterEach(() => { | ||
php.exit(); | ||
}); | ||
|
||
await php.run({ | ||
code: `<?php $zip = new ZipArchive(); | ||
$zip->open("${zipFileName}", ZIPARCHIVE::CREATE); | ||
$zip->addFile("/index.php"); | ||
$zip->close();`, | ||
it('should install a plugin', async () => { | ||
await installPlugin(php, { | ||
pluginZipFile: await zipFiles(php, zipFileName, { | ||
[`${pluginName}/index.php`]: `/**\n * Plugin Name: Test Plugin`, | ||
}), | ||
ifAlreadyInstalled: 'overwrite', | ||
options: { | ||
activate: false, | ||
}, | ||
}); | ||
expect(php.fileExists(installedPluginPath)).toBe(true); | ||
}); | ||
|
||
expect(php.fileExists(zipFileName)).toBe(true); | ||
describe('ifAlreadyInstalled option', () => { | ||
beforeEach(async () => { | ||
await installPlugin(php, { | ||
pluginZipFile: await zipFiles(php, zipFileName, { | ||
[`${pluginName}/index.php`]: `/**\n * Plugin Name: Test Plugin`, | ||
}), | ||
ifAlreadyInstalled: 'overwrite', | ||
options: { | ||
activate: false, | ||
}, | ||
}); | ||
}); | ||
|
||
// Create plugins folder | ||
const rootPath = await php.documentRoot; | ||
const pluginsPath = `${rootPath}/wp-content/plugins`; | ||
it('ifAlreadyInstalled=overwrite should overwrite the plugin if it already exists', async () => { | ||
// Install the plugin | ||
await installPlugin(php, { | ||
pluginZipFile: await zipFiles(php, zipFileName, { | ||
[`${pluginName}/index.php`]: `/**\n * Plugin Name: A different Plugin`, | ||
}), | ||
ifAlreadyInstalled: 'overwrite', | ||
options: { | ||
activate: false, | ||
}, | ||
}); | ||
expect( | ||
php.readFileAsText(`${installedPluginPath}/index.php`) | ||
).toContain('Plugin Name: A different Plugin'); | ||
}); | ||
|
||
php.mkdir(pluginsPath); | ||
it('ifAlreadyInstalled=skip should skip the plugin if it already exists', async () => { | ||
// Install the plugin | ||
await installPlugin(php, { | ||
pluginZipFile: await zipFiles(php, zipFileName, { | ||
[`${pluginName}/index.php`]: `/**\n * Plugin Name: A different Plugin`, | ||
}), | ||
ifAlreadyInstalled: 'skip', | ||
options: { | ||
activate: false, | ||
}, | ||
}); | ||
expect( | ||
php.readFileAsText(`${installedPluginPath}/index.php`) | ||
).toContain('Plugin Name: Test Plugin'); | ||
}); | ||
|
||
await runBlueprintSteps( | ||
compileBlueprint({ | ||
steps: [ | ||
{ | ||
step: 'installPlugin', | ||
pluginZipFile: { | ||
resource: 'vfs', | ||
path: zipFileName, | ||
}, | ||
options: { | ||
activate: false, | ||
}, | ||
it('ifAlreadyInstalled=error should throw an error if the plugin already exists', async () => { | ||
// Install the plugin | ||
await expect( | ||
installPlugin(php, { | ||
pluginZipFile: await zipFiles(php, zipFileName, { | ||
[`${pluginName}/index.php`]: `/**\n * Plugin Name: A different Plugin`, | ||
}), | ||
ifAlreadyInstalled: 'error', | ||
options: { | ||
activate: false, | ||
}, | ||
], | ||
}), | ||
php | ||
); | ||
|
||
php.unlink(zipFileName); | ||
expect(php.fileExists(`${pluginsPath}/${pluginName}-0.0.1`)).toBe(true); | ||
}) | ||
).rejects.toThrowError(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.