Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wp-now: Add executeWPCli() function to download and execute WP-CLI #395

Merged
merged 24 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b5173cb
wp-now: add basic wp-cli command
sejas May 19, 2023
e101433
Merge branch 'trunk' of github.com:Automattic/wordpress-playground in…
sejas May 22, 2023
af6a0ad
wp-now: remove wp command until we fix pthreads
sejas May 23, 2023
d5ba677
wp-now: remove unnecessary flags for wp-cli
sejas May 23, 2023
f53d520
Merge branch 'trunk' of github.com:Automattic/wordpress-playground in…
sejas May 23, 2023
8fcff51
wp-now: force creating an empty file for downloadFile
sejas May 23, 2023
ad3513d
wp-now: add tests for executeWPCli
sejas May 23, 2023
de46790
wp-now: optimize tests and download files in parallel
sejas May 23, 2023
877cfe7
wp-now: accept path as second argument on executeWPCli
sejas May 23, 2023
5741ae8
wp-now: improve download file
sejas May 23, 2023
244ee69
wp-now: add emscriptenOptions to startWPNow
sejas May 23, 2023
7f1f9ce
wp-now: change wp-cli option to get its version
sejas May 23, 2023
863b338
wp-now: download wp-cli only for that test collection
sejas May 23, 2023
96ae2cf
wp-now: move downloadWithTimer to another PR
sejas May 23, 2023
cc86a62
Merge branch 'trunk' of github.com:Automattic/wordpress-playground in…
sejas May 24, 2023
1766aa1
wp-now: use downloadWithTimer for wp-cli
sejas May 24, 2023
da7699d
wp-now: change wp-cli test version
sejas May 24, 2023
99174cc
wp-now: avoid exposing emscriptenOptions and just onStdout
sejas May 24, 2023
acfbe6f
wp-now: remove unused import
sejas May 24, 2023
471b353
wp-now: mock console.log for wp-cli tests
sejas May 24, 2023
ca269c6
wp-now: remove the stdout and emscriptenOptions unnecessary
sejas May 24, 2023
e0395e6
wp-now: add comment on executeWPCli marking it as unstable
sejas May 24, 2023
a241c2d
Merge branch 'trunk' into add/wp-now-command-wp-cli
danielbachhuber May 25, 2023
5e8e97c
Use a tmp directory for WP-CLI tests
danielbachhuber May 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# WordPress Playground and PHP WASM (WebAssembly)
# WordPress Playground and PHP WASM (WebAssembly)

[Project Page](https://developer.wordpress.org/playground/) | [Live demo](https://playground.wordpress.net/) | [Documentation and API Reference](https://wordpress.github.io/wordpress-playground/)

Expand Down
6 changes: 6 additions & 0 deletions packages/wp-now/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ export const DEFAULT_PHP_VERSION = '8.0';
* The default WordPress version to use when running the WP Now server.
*/
export const DEFAULT_WORDPRESS_VERSION = 'latest';

/**
* The URL for downloading the "wp-cli" WordPress cli.
*/
export const WP_CLI_URL =
'https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar';
52 changes: 51 additions & 1 deletion packages/wp-now/src/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import followRedirects from 'follow-redirects';
import unzipper from 'unzipper';
import os from 'os';
import { IncomingMessage } from 'http';
import { DEFAULT_WORDPRESS_VERSION, SQLITE_URL } from './constants';
import { DEFAULT_WORDPRESS_VERSION, SQLITE_URL, WP_CLI_URL } from './constants';
import { isValidWordPressVersion } from './wp-playground-wordpress';
import { output } from './output';
import getWpNowPath from './get-wp-now-path';
import getWordpressVersionsPath from './get-wordpress-versions-path';
import getSqlitePath from './get-sqlite-path';
import getWpCliPath from './get-wp-cli-path';

function getWordPressVersionUrl(version = DEFAULT_WORDPRESS_VERSION) {
if (!isValidWordPressVersion(version)) {
Expand All @@ -28,6 +29,55 @@ interface DownloadFileAndUnzipResult {
followRedirects.maxRedirects = 5;
const { https } = followRedirects;

async function downloadFile({
url,
destinationFilePath,
itemName,
}): Promise<DownloadFileAndUnzipResult> {
let statusCode = 0;
try {
if (fs.existsSync(destinationFilePath)) {
return { downloaded: false, statusCode: 0 };
}
fs.ensureDirSync(path.dirname(destinationFilePath));
const response = await new Promise<IncomingMessage>((resolve) =>
https.get(url, (response) => resolve(response))
);
statusCode = response.statusCode;
if (response.statusCode !== 200) {
throw new Error(
`Failed to download file (Status code ${response.statusCode}).`
);
}
await new Promise<void>((resolve, reject) => {
fs.ensureFileSync(destinationFilePath);
const file = fs.createWriteStream(destinationFilePath);
response.pipe(file);
file.on('finish', () => {
file.close();
resolve();
});
file.on('error', (error) => {
file.close();
reject(error);
});
});
output?.log(`Downloaded ${itemName} to ${destinationFilePath}`);
return { downloaded: true, statusCode };
} catch (error) {
output?.error(`Error downloading file ${itemName}`, error);
return { downloaded: false, statusCode };
}
}

export async function downloadWPCLI() {
return downloadFile({
url: WP_CLI_URL,
destinationFilePath: getWpCliPath(),
itemName: 'wp-cli',
});
}

async function downloadFileAndUnzip({
url,
destinationFolder,
Expand Down
40 changes: 40 additions & 0 deletions packages/wp-now/src/execute-wp-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import startWPNow from './wp-now';
import { downloadWPCLI } from './download';
import { disableOutput } from './output';
import getWpCliPath from './get-wp-cli-path';
import getWpNowConfig from './config';
import { DEFAULT_PHP_VERSION, DEFAULT_WORDPRESS_VERSION } from './constants';

export async function executeWPCli(args: string[], emscriptenOptions = {}) {
await downloadWPCLI();
disableOutput();
const options = await getWpNowConfig({
php: DEFAULT_PHP_VERSION,
wp: DEFAULT_WORDPRESS_VERSION,
path: process.env.WP_NOW_PROJECT_PATH || process.cwd(),
});
const { phpInstances, options: wpNowOptions } = await startWPNow(
{
...options,
numberOfPhpInstances: 2,
},
emscriptenOptions
);
const [, php] = phpInstances;

try {
php.useHostFilesystem();
await php.cli([
'php',
getWpCliPath(),
`--path=${wpNowOptions.documentRoot}`,
...args,
]);
} catch (resultOrError) {
const success =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I merged this change: https://github.com/WordPress/wordpress-playground/pull/470/files#diff-3652c54df4b8d0d8e367481121e32f08cc409cfc4236fdb3172d69dcbf6ca8b0

It shouldn't matter here at all and this check still passes, but I wanted to flag it here just in case.

resultOrError.name === 'ExitStatus' && resultOrError.status === 0;
if (!success) {
throw resultOrError;
}
}
}
12 changes: 12 additions & 0 deletions packages/wp-now/src/get-wp-cli-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import path from 'path';
import getWpNowPath from './get-wp-now-path';

/**
* The path for wp-cli phar file within the WP Now folder.
*/
export default function getWpCliPath() {
if (process.env.NODE_ENV !== 'test') {
return path.join(getWpNowPath(), 'wp-cli.phar');
}
return path.resolve('./wp-cli.phar');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a temp directory?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that using a temporal directory on my laptop, the wp-cli.phar cannot be executed.
I tried setting the right permissions fs.chmod() to the file and folder and it didn't work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sejas Ok. I think we should use a tmp directory in any case. I added with 5e8e97c

@adamziel Any idea why the test might fail?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danielbachhuber , with the temp folder, it seems to pass ok in CI. But it is still failing on my local computer.
Maybe it's something just in my environment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it getting mounted correctly @sejas? What do you see when you list files?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sejas It failed locally for me too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it getting mounted correctly @sejas? What do you see when you list files?

Yes, the file is accessible in VFS and the file size is correct. It must be something related to MacOs and the tmp path.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sejas would you start a new issue for this? I'd like to make sure this works on MacOs

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a conflict mounting /var and using the hostFilesystem.
A second PR was needed:
WordPress/playground-tools@bf31e3e

}
1 change: 1 addition & 0 deletions packages/wp-now/src/run-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import getWpNowConfig from './config';
import { spawn, SpawnOptionsWithoutStdio } from 'child_process';
import { executePHPFile } from './execute-php-file';
import { output } from './output';
import { executeWPCli } from './execute-wp-cli';

function startSpinner(message: string) {
process.stdout.write(`${message}...\n`);
Expand Down
36 changes: 35 additions & 1 deletion packages/wp-now/src/tests/wp-now.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import startWPNow, { inferMode } from '../wp-now';
import getWpNowConfig, { CliOptions, WPNowMode, WPNowOptions } from '../config';
import getWpNowConfig, { CliOptions, WPNowMode } from '../config';
import fs from 'fs-extra';
import path from 'path';
import {
Expand All @@ -11,11 +11,14 @@ import {
} from '../wp-playground-wordpress';
import {
downloadSqliteIntegrationPlugin,
downloadWPCLI,
downloadWordPress,
} from '../download';
import os from 'os';
import crypto from 'crypto';
import getWpNowTmpPath from '../get-wp-now-tmp-path';
import { executeWPCli } from '../execute-wp-cli';
import getWpCliPath from '../get-wp-cli-path';

const exampleDir = __dirname + '/mode-examples';

Expand Down Expand Up @@ -506,3 +509,34 @@ describe('Test starting different modes', () => {
expect(themeName.text).toContain('Twenty Twenty-Three');
});
});

/**
* Test wp-cli command.
*/
describe('wp-cli command', () => {
beforeAll(async () => {
fs.rmSync(getWpNowTmpPath(), { recursive: true, force: true });
console.time('wp-cli');
await downloadWPCLI();
console.log('wp-cli downloaded.');
console.timeEnd('wp-cli');
});

afterAll(() => {
fs.removeSync(getWpCliPath());
});
/**
* Test wp-cli displays the version.
* We don't need the WordPress context for this test.
*/
test('wp-cli displays the version', async () => {
let output = '';
function collectOutput(outputLine: string) {
output += outputLine;
}
await executeWPCli(['--version'], {
danielbachhuber marked this conversation as resolved.
Show resolved Hide resolved
print: collectOutput,
});
expect(output).toMatch('WP-CLI ');
danielbachhuber marked this conversation as resolved.
Show resolved Hide resolved
});
});
4 changes: 3 additions & 1 deletion packages/wp-now/src/wp-now.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ async function applyToInstances(phpInstances: NodePHP[], callback: Function) {
}

export default async function startWPNow(
options: Partial<WPNowOptions> = {}
options: Partial<WPNowOptions> = {},
emscriptenOptions = {}
adamziel marked this conversation as resolved.
Show resolved Hide resolved
): Promise<{ php: NodePHP; phpInstances: NodePHP[]; options: WPNowOptions }> {
const { documentRoot } = options;
const nodePHPOptions = {
Expand All @@ -59,6 +60,7 @@ export default async function startWPNow(
}
},
},
emscriptenOptions,
};

const phpInstances = [];
Expand Down