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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate non-bundled install; generate against current WP-CLI instance. #207

Merged
merged 4 commits into from Feb 19, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to鈥
Jump to file or symbol
Failed to load files and symbols.
+120 鈭154
Diff settings

Always

Just for now

Copy path View file
@@ -3,7 +3,21 @@ wp-cli/handbook

These files comprise the WP-CLI handbook ([make.wordpress.org/cli/handbook](https://make.wordpress.org/cli/handbook/)) and WP-CLI commands directory ([developer.wordpress.org/cli/commands](https://developer.wordpress.org/cli/commands/)).

The documentation is located in GitHub to enable a pull request-based editing workflow. Long-form documentation (e.g. "Commands cookbook") can be edited directly. Internal API docs and command pages are generated dynamically from the WP-CLI codebase using the `wp handbook` series of commands. `wp handbook gen-all` will regenerate all of the handbook pages.
The documentation is located in GitHub to enable a pull request-based editing workflow.

Long-form documentation (e.g. "Commands cookbook") can be edited directly.

Internal API docs and command pages are generated dynamically from the WP-CLI codebase using the `wp handbook` series of commands.

Before running these commands the bash script `bin/install-packages.sh` should be run to install the latest versions of the non-bundled commands in `bin/packages`. Note `wp` must point to the target WP-CLI instance, i.e. the phar/git that contains the docblocks to be generated against, and should be run with `WP_CLI_PACKAGES_DIR=bin/packages` and `WP_CLI_CONFIG_PATH=/dev/null`.

So for instance to generate all dynamically created documentation against the nightly phar run:

```
wp cli update --nightly
bin/install-packages.sh
WP_CLI_PACKAGES_DIR=bin/packages WP_CLI_CONFIG_PATH=/dev/null wp handbook gen-all
```

All documentation is imported automatically into WordPress.org in a two step process:

Copy path View file
@@ -4,6 +4,7 @@
use WP_CLI;
use Mustache_Engine;
use WP_CLI\Utils;
/**
* WP-CLI commands to generate docs from the codebase.
@@ -18,25 +19,35 @@
class Command {
/**
* Regenerate all doc pages
* Regenerates all doc pages.
*
* ## OPTIONS
*
* [--verbose]
* : If set will list command pages as they are generated.
*
* @subcommand gen-all
*/
public function gen_all() {
public function gen_all( $args, $assoc_args ) {
// Warn if not invoked with null WP_CLI_CONFIG_PATH.
if ( '/dev/null' !== getenv( 'WP_CLI_CONFIG_PATH' ) ) {

This comment has been minimized.

Copy link
@schlessera

schlessera Feb 14, 2018

Member

That won't work on Windows. However, not sure if we need to be able to generate the docs on Windows machines...

This comment has been minimized.

Copy link
@gitlost

gitlost Feb 14, 2018

Author Contributor

I did actually consider that also believe it or not - only a warning but it is lazy...

This comment has been minimized.

Copy link
@schlessera

schlessera Feb 14, 2018

Member

If this is a requirement, why not just putenv() it to force it?

This comment has been minimized.

Copy link
@gitlost

gitlost Feb 14, 2018

Author Contributor

I did actually consider that, but these are used during bootstrapping so there's no hook available that early? Plus there's an advantage I think to just being explicit and dumb so it's obvious what is happening for such a seldom used command....

WP_CLI::warning( "Should be invoked on the target WP-CLI with 'WP_CLI_CONFIG_PATH=/dev/null'." );
}
self::gen_api_docs();
self::gen_commands();
self::gen_commands( $args, $assoc_args );
self::gen_commands_manifest();
self::gen_hb_manifest();
WP_CLI::success( 'Generated all doc pages.' );
}
/**
* Generate internal API doc pages
* Generates internal API doc pages.
*
* @subcommand gen-api-docs
*/
public function gen_api_docs() {
$apis = self::invoke_wp_cli( 'wp handbook api-dump' );
$apis = WP_CLI::runcommand( 'handbook api-dump', array( 'launch' => false, 'return' => 'stdout', 'parse' => 'json' ) );
$categories = array(
'Registration' => array(),
'Output' => array(),
@@ -80,6 +91,8 @@ public function gen_api_docs() {
EOT;
self::empty_dir( WP_CLI_HANDBOOK_PATH . '/internal-api/' );
foreach( $categories as $name => $apis ) {
$out .= '## ' . $name . PHP_EOL . PHP_EOL;
$out .= self::render( 'internal-api-list.mustache', array( 'apis' => $apis ) );
@@ -109,53 +122,46 @@ public function gen_api_docs() {
}
file_put_contents( WP_CLI_HANDBOOK_PATH . '/internal-api.md', $out );
WP_CLI::success( 'Generated /docs/internal-api/' );
WP_CLI::success( 'Generated internal-api/' );
}
/**
* Generate command pages
* Generates all command pages.
*
* ## OPTIONS
*
* [--verbose]
* : If set will list command pages as they are generated.
*
* @subcommand gen-commands
*/
public function gen_commands() {
$wp = self::invoke_wp_cli( 'wp --skip-packages cli cmd-dump' );
$bundled_cmds = array();
foreach( $wp['subcommands'] as $k => $cmd ) {
if ( in_array( $cmd['name'], array( 'website', 'api-dump', 'handbook' ) ) ) {
unset( $wp['subcommands'][ $k ] );
continue;
}
$bundled_cmds[] = $cmd['name'];
public function gen_commands( $args, $assoc_args ) {
// Check invoked with packages directory set to `bin/packages'.
if ( ! preg_match( '/bin\/packages\/?$/', getenv( 'WP_CLI_PACKAGES_DIR' ) ) ) {

This comment has been minimized.

Copy link
@schlessera

schlessera Feb 14, 2018

Member

If this is a requirement, why not just putenv() it to force it?

WP_CLI::error( "Needs to be invoked on the target WP-CLI with 'WP_CLI_PACKAGES_DIR=bin/packages'." );
}
$wp['subcommands'] = array_values( $wp['subcommands'] );
foreach ( $wp['subcommands'] as $cmd ) {
self::gen_cmd_pages( $cmd );
// Check non-bundled commands installed.
$runner = WP_CLI::get_runner();
$have_nonbundled_installed = true;
foreach ( array( 'admin', 'find', 'profile', 'dist-archive' ) as $cmd ) {
$have_nonbundled_installed = $have_nonbundled_installed && is_array( $runner->find_command_to_run( array( $cmd ) ) );
}
$package_dir = dirname( __DIR__ ) . '/bin/packages';
foreach ( array(
'wp-cli/admin-command',
'wp-cli/find-command',
'wp-cli/profile-command',
'wp-cli/dist-archive-command'
) as $package ) {
WP_CLI::log( 'Installing ' . $package );
self::invoke_wp_cli( 'WP_CLI_PACKAGES_DIR=' . $package_dir . ' wp package install ' . $package );
if ( ! $have_nonbundled_installed ) {
WP_CLI::error( sprintf( "Install non-bundled packages by running '%s' first.", 'bin/install_packages.sh' ) );
}
$wp_with_packages = self::invoke_wp_cli( 'WP_CLI_PACKAGES_DIR=' . $package_dir . ' wp cli cmd-dump' );
foreach( $wp_with_packages['subcommands'] as $k => $cmd ) {
if ( in_array( $cmd['name'], array( 'website', 'api-dump', 'handbook' ) )
|| in_array( $cmd['name'], $bundled_cmds ) ) {
unset( $wp_with_packages['subcommands'][ $k ] );
}
}
$wp_with_packages['subcommands'] = array_values( $wp_with_packages['subcommands'] );
self::empty_dir( WP_CLI_HANDBOOK_PATH . '/commands/' );
foreach ( $wp_with_packages['subcommands'] as $cmd ) {
self::gen_cmd_pages( $cmd );
$wp = WP_CLI::runcommand( 'cli cmd-dump', array( 'launch' => false, 'return' => 'stdout', 'parse' => 'json' ) );
$verbose = Utils\get_flag_value( $assoc_args, 'verbose', false );
foreach ( $wp['subcommands'] as $cmd ) {
if ( in_array( $cmd['name'], array( 'website', 'api-dump', 'handbook' ), true ) ) {
continue;
}
self::gen_cmd_pages( $cmd, array() /*parent*/, $verbose );
}
WP_CLI::success( 'Generated all command pages.' );
@@ -164,23 +170,32 @@ public function gen_commands() {
/**
* Update the commands data array with new data
*/
private static function update_commands_data( $command, &$commands_data, $parent ) {
$full = trim( $parent . ' ' . $command->get_name() );
private static function update_commands_data( $command, &$commands_data, $full ) {
$reflection = new \ReflectionClass( $command );
$repo_url = '';
if ( 'help' === substr( $full, 0, 4 )
|| 'cli' === substr( $full, 0, 3 ) ) {
$repo_url = 'https://github.com/wp-cli/wp-cli';
}
if ( $reflection->hasProperty( 'when_invoked' ) ) {
$filename = '';
$when_invoked = $reflection->getProperty( 'when_invoked' );
$when_invoked->setAccessible( true );
$closure = $when_invoked->getValue( $command );
$closure_reflection = new \ReflectionFunction( $closure );
// PHP stores use clause arguments of closures as static variables internally - see https://bugs.php.net/bug.php?id=71250
$static = $closure_reflection->getStaticVariables();
if ( isset( $static['callable'][0] ) ) {
$reflection_class = new \ReflectionClass( $static['callable'][0] );
$filename = $reflection_class->getFileName();
if ( is_array( $static ) && isset( $static['callable'] ) ) {
// See `CommandFactory::create_subcommand()`.
if ( is_array( $static['callable'] ) && isset( $static['callable'][0] ) ) {
$reflection_class = new \ReflectionClass( $static['callable'][0] );
$filename = $reflection_class->getFileName();
} elseif ( is_callable( $static['callable'] ) ) {
$reflection_func = new \ReflectionFunction( $static['callable'] );
$filename = $reflection_func->getFileName();
}
}
if ( $filename ) {
preg_match( '#vendor/([^/]+/[^/]+)#', $filename, $matches );
if ( ! empty( $matches[1] ) ) {
$repo_url = 'https://github.com/' . $matches[1];
@@ -189,21 +204,22 @@ private static function update_commands_data( $command, &$commands_data, $parent
WP_CLI::error( 'No callable for: ' . var_export( $static, true ) );
}
}
$commands_data[ $full ] = array(
'repo_url' => $repo_url,
);
$len = count( $commands_data );
foreach ( $command->get_subcommands() as $subcommand ) {
$sub_full = trim( $full . ' ' . $subcommand->get_name() );
self::update_commands_data( $subcommand, $commands_data, $full );
self::update_commands_data( $subcommand, $commands_data, $sub_full );
if ( '' === $repo_url && isset( $commands_data[ $sub_full ]['repo_url'] ) ) {
$repo_url = $commands_data[ $sub_full ]['repo_url'];
}
}
if ( isset( $sub_full ) && ! $commands_data[ $full ]['repo_url'] && ! empty( $commands_data[ $sub_full ]['repo_url'] ) ) {
$commands_data[ $full ]['repo_url'] = $commands_data[ $sub_full ]['repo_url'];
if ( $repo_url ) {
$commands_data[ $full ] = array(
'repo_url' => $repo_url,
);
}
}
/**
* Generate a manifest document of all command pages
* Generates a manifest document of all command pages.
*
* @subcommand gen-commands-manifest
*/
@@ -216,7 +232,7 @@ public function gen_commands_manifest() {
);
$commands_data = array();
foreach( WP_CLI::get_root_command()->get_subcommands() as $command ) {
self::update_commands_data( $command, $commands_data, '' );
self::update_commands_data( $command, $commands_data, $command->get_name() );
}
foreach( $paths as $path ) {
foreach( glob( $path ) as $file ) {
@@ -247,11 +263,11 @@ public function gen_commands_manifest() {
}
file_put_contents( WP_CLI_HANDBOOK_PATH . '/bin/commands-manifest.json', json_encode( $manifest, JSON_PRETTY_PRINT ) );
$count = count( $manifest );
WP_CLI::success( "Generated commands-manifest.json of {$count} commands" );
WP_CLI::success( "Generated bin/commands-manifest.json of {$count} commands" );
}
/**
* Generate a manifest document of all handbook pages
* Generates a manifest document of all handbook pages.
*
* @subcommand gen-hb-manifest
*/
@@ -291,17 +307,16 @@ public function gen_hb_manifest() {
);
}
file_put_contents( WP_CLI_HANDBOOK_PATH . '/bin/handbook-manifest.json', json_encode( $manifest, JSON_PRETTY_PRINT ) );
WP_CLI::success( 'Generated handbook-manifest.json' );
WP_CLI::success( 'Generated bin/handbook-manifest.json' );
}
/**
* Dump internal API PHPDoc to JSON
* Dumps internal API PHPDoc to JSON.
*
* @subcommand api-dump
*/
public function api_dump() {
$apis = array();
require WP_CLI_ROOT . '/php/utils-wp.php';
$functions = get_defined_functions();
foreach( $functions['user'] as $function ) {
$reflection = new \ReflectionFunction( $function );
@@ -329,12 +344,12 @@ public function api_dump() {
echo json_encode( $apis );
}
private static function gen_cmd_pages( $cmd, $parent = array() ) {
private static function gen_cmd_pages( $cmd, $parent = array(), $verbose = false ) {
$parent[] = $cmd['name'];
static $params;
if ( ! isset( $params ) ) {
$params = self::invoke_wp_cli( 'wp --skip-packages cli param-dump' );
$params = WP_CLI::runcommand( 'cli param-dump', array( 'launch' => false, 'return' => 'stdout', 'parse' => 'json' ) );
// Preserve positioning of 'url' param
$url_param = $params['url'];
unset( $params['url'] );
@@ -448,13 +463,15 @@ private static function gen_cmd_pages( $cmd, $parent = array() ) {
mkdir( dirname( $path ) );
}
file_put_contents( "$path.md", self::render( 'subcmd-list.mustache', $binding ) );
WP_CLI::log( 'Generated /commands/' . $binding['path'] . '/' );
if ( $verbose ) {
WP_CLI::log( 'Generated commands/' . $binding['path'] . '/' );
}
if ( !isset( $cmd['subcommands'] ) )
return;
foreach ( $cmd['subcommands'] as $subcmd ) {
self::gen_cmd_pages( $subcmd, $parent );
self::gen_cmd_pages( $subcmd, $parent, $verbose );
}
}
@@ -574,25 +591,29 @@ private static function parse_docblock( $docblock ) {
return $ret;
}
private static function invoke_wp_cli( $cmd ) {
ob_start();
system( "WP_CLI_CONFIG_PATH=/dev/null $cmd", $return_code );
$json = ob_get_clean();
if ( $return_code ) {
echo "WP-CLI returned error code: $return_code\n";
exit(1);
}
return json_decode( $json, true );
}
private static function render( $path, $binding ) {
$m = new Mustache_Engine;
$template = file_get_contents( WP_CLI_HANDBOOK_PATH . "/bin/templates/$path" );
return $m->render( $template, $binding );
}
/**
* Removes existing contents of given directory.
*
* @param string $dir Name of directory to empty.
*/
private static function empty_dir( $dir ) {
$cmd = Utils\esc_cmd( 'rm -rf %s', $dir );
$pr = WP_CLI::launch( $cmd, false /*exit_on_error*/, true /*return_detailed*/ ); // Won't fail if directory doesn't exist.
if ( $pr->return_code ) {
WP_CLI::error( sprintf( "Failed to `%s`: (%d) %s", $cmd, $pr->return_code, $pr->stderr ) );
}
if ( ! mkdir( $dir ) ) {
$error = error_get_last();
WP_CLI::error( sprintf( "Failed to create '%s' directory: %s", $dir, $error['message'] ) );
}
WP_CLI::log( sprintf( "Removed existing contents of '%s'", $dir ) );
}
}
WP_CLI::add_command( 'handbook', '\WP_CLI\Handbook\Command' );
Copy path View file
@@ -395,12 +395,6 @@
"markdown_source": "https:\/\/github.com\/wp-cli\/handbook\/blob\/master\/internal-api\/wp-cli-utils-trailingslashit.md",
"parent": "internal-api"
},
"wp-cli-utils-wp-clear-object-cache": {
"title": "WP_CLI\\Utils\\wp_clear_object_cache()",
"slug": "wp-cli-utils-wp-clear-object-cache",
"markdown_source": "https:\/\/github.com\/wp-cli\/handbook\/blob\/master\/internal-api\/wp-cli-utils-wp-clear-object-cache.md",
"parent": "internal-api"
},
"wp-cli-utils-write-csv": {
"title": "WP_CLI\\Utils\\write_csv()",
"slug": "wp-cli-utils-write-csv",
Copy path View file
@@ -0,0 +1,8 @@
#!/bin/bash

# Installs the 4 non-bundled packages in `bin/packages`. To be run before using the `wp handbook` commands.

WP_CLI_PACKAGES_DIR=bin/packages WP_CLI_CONFIG_PATH=/dev/null wp package install wp-cli/admin-command
WP_CLI_PACKAGES_DIR=bin/packages WP_CLI_CONFIG_PATH=/dev/null wp package install wp-cli/find-command
WP_CLI_PACKAGES_DIR=bin/packages WP_CLI_CONFIG_PATH=/dev/null wp package install wp-cli/profile-command
WP_CLI_PACKAGES_DIR=bin/packages WP_CLI_CONFIG_PATH=/dev/null wp package install wp-cli/dist-archive-command
Copy path View file
@@ -134,9 +134,6 @@ This also means functions and methods not listed here are considered part of the
<li><strong><a href="https://make.wordpress.org/cli/handbook/internal-api/wp-cli-utils-get-php-binary/">WP_CLI\Utils\get_php_binary()</a></strong> - Get the path to the PHP binary used when executing WP-CLI.</li>


<li><strong><a href="https://make.wordpress.org/cli/handbook/internal-api/wp-cli-utils-wp-clear-object-cache/">WP_CLI\Utils\wp_clear_object_cache()</a></strong> - Clear WordPress internal object caches.</li>


<li><strong><a href="https://make.wordpress.org/cli/handbook/internal-api/wp-cli-get-php-binary/">WP_CLI::get_php_binary()</a></strong> - Get the path to the PHP binary used when executing WP-CLI.</li>


@@ -45,9 +45,6 @@ Note: moved to Utils, left for BC.
<li><strong><a href="https://make.wordpress.org/cli/handbook/internal-api/wp-cli-utils-get-php-binary/">WP_CLI\Utils\get_php_binary()</a></strong> - Get the path to the PHP binary used when executing WP-CLI.</li>


<li><strong><a href="https://make.wordpress.org/cli/handbook/internal-api/wp-cli-utils-wp-clear-object-cache/">WP_CLI\Utils\wp_clear_object_cache()</a></strong> - Clear WordPress internal object caches.</li>



</ul>

Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.
You can鈥檛 perform that action at this time.