├── .actrc ├── .circleci └── config.yml ├── .distignore ├── .editorconfig ├── .gherkin-lintrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE ├── PULL_REQUEST_TEMPLATE ├── dependabot.yml └── workflows │ ├── code-quality.yml │ ├── lint-gherkin.yml │ ├── regenerate-readme.yml │ └── testing.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── behat.yml ├── composer.json ├── features ├── scaffold-package-github.feature ├── scaffold-package-readme.feature ├── scaffold-package-tests.feature └── scaffold-package.feature ├── package-lock.json ├── package.json ├── phpcs.xml.dist ├── scaffold-package-command.php ├── src └── ScaffoldPackageCommand.php ├── templates ├── .travis.yml ├── HelloWorldCommand.mustache ├── composer.mustache ├── github-issue-template.mustache ├── github-pull-request-template.mustache ├── github-settings.mustache ├── hello-world-command.mustache ├── load-wp-cli.feature ├── readme-contributing.mustache ├── readme-installing-bundled.mustache ├── readme-installing.mustache ├── readme-support.mustache ├── readme-using.mustache ├── readme.mustache └── testing.yml └── wp-cli.yml /.actrc: -------------------------------------------------------------------------------- 1 | # Configuration file for nektos/act. 2 | # See https://github.com/nektos/act#configuration 3 | -P ubuntu-latest=shivammathur/node:latest 4 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/wp-cli/package-tests 5 | parallelism: 1 6 | docker: 7 | - image: circleci/php:7.4-bullseye 8 | environment: 9 | WP_CLI_TEST_DBHOST: 127.0.0.1:3306 10 | WP_CLI_TEST_DBROOTPASS: root 11 | WP_CLI_TEST_DBUSER: wp_cli_test 12 | WP_CLI_TEST_DBPASS: password1 13 | - image: circleci/mariadb:10.5 14 | environment: 15 | MYSQL_ROOT_PASSWORD: root 16 | MYSQL_DATABASE: wp_cli_test 17 | MYSQL_USER: wp_cli_test 18 | MYSQL_PASSWORD: password1 19 | steps: 20 | - checkout 21 | - run: | 22 | sudo sh -c "printf '\ndeb http://ftp.us.debian.org/debian bullseye main\n' >> /etc/apt/sources.list" 23 | sudo apt-get update 24 | sudo docker-php-ext-install mysqli 25 | sudo apt-get install mariadb-client 26 | - run: | 27 | echo -e "memory_limit = 1024M" | sudo tee /usr/local/etc/php/php.ini > /dev/null 28 | - run: | 29 | dockerize -wait tcp://127.0.0.1:3306 -timeout 1m 30 | - run: | 31 | composer validate 32 | composer install 33 | composer prepare-tests 34 | - run: | 35 | echo 'export PATH=$HOME/wp-cli/package-tests/vendor/bin:$PATH' >> $BASH_ENV 36 | source $BASH_ENV 37 | - run: | 38 | WP_VERSION=latest composer test 39 | rm -rf '/tmp/wp-cli-test core-download-cache' 40 | WP_VERSION=trunk composer test 41 | -------------------------------------------------------------------------------- /.distignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .git 3 | .gitignore 4 | .gitlab-ci.yml 5 | .editorconfig 6 | .travis.yml 7 | behat.yml 8 | circle.yml 9 | phpcs.xml.dist 10 | phpunit.xml.dist 11 | bin/ 12 | features/ 13 | utils/ 14 | *.zip 15 | *.tar.gz 16 | *.swp 17 | *.txt 18 | *.log 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | # From https://github.com/WordPress/wordpress-develop/blob/trunk/.editorconfig with a couple of additions. 8 | 9 | root = true 10 | 11 | [*] 12 | charset = utf-8 13 | end_of_line = lf 14 | insert_final_newline = true 15 | trim_trailing_whitespace = true 16 | indent_style = tab 17 | 18 | [{*.yml,*.feature,.jshintrc,*.json}] 19 | indent_style = space 20 | indent_size = 2 21 | 22 | [*.md] 23 | trim_trailing_whitespace = false 24 | 25 | [{*.txt,wp-config-sample.php}] 26 | end_of_line = crlf 27 | -------------------------------------------------------------------------------- /.gherkin-lintrc: -------------------------------------------------------------------------------- 1 | { 2 | "indentation": [ 3 | "on", 4 | { 5 | "Feature": 0, 6 | "Background": 2, 7 | "Scenario": 2, 8 | "Step": 4, 9 | "given": 4, 10 | "and": 4 11 | } 12 | ], 13 | "no-dupe-feature-names": "on", 14 | "no-dupe-scenario-names": "off", 15 | "no-empty-file": "on", 16 | "no-files-without-scenarios": "on", 17 | "no-multiple-empty-lines": "on", 18 | "no-partially-commented-tag-lines": "on", 19 | "no-trailing-spaces": "on", 20 | "no-unnamed-features": "on", 21 | "no-unnamed-scenarios": "on", 22 | "no-scenario-outlines-without-examples": "on" 23 | } 24 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @wp-cli/committers 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | labels: 9 | - scope:distribution 10 | - package-ecosystem: github-actions 11 | directory: "/" 12 | schedule: 13 | interval: daily 14 | open-pull-requests-limit: 10 15 | labels: 16 | - scope:distribution 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/code-quality.yml: -------------------------------------------------------------------------------- 1 | name: Code Quality Checks 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | 10 | jobs: 11 | code-quality: 12 | uses: wp-cli/.github/.github/workflows/reusable-code-quality.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/lint-gherkin.yml: -------------------------------------------------------------------------------- 1 | name: Gherkin Linting 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | 7 | gherkin-lint: 8 | name: Lint Gherkin Feature files 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: bahmutov/npm-install@v1 13 | - run: npm run lint 14 | -------------------------------------------------------------------------------- /.github/workflows/regenerate-readme.yml: -------------------------------------------------------------------------------- 1 | name: Regenerate README file 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | paths-ignore: 10 | - "features/**" 11 | - "README.md" 12 | 13 | jobs: 14 | regenerate-readme: 15 | uses: wp-cli/.github/.github/workflows/reusable-regenerate-readme.yml@main 16 | -------------------------------------------------------------------------------- /.github/workflows/testing.yml: -------------------------------------------------------------------------------- 1 | name: Testing 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | - master 10 | schedule: 11 | - cron: '17 1 * * *' # Run every day on a seemly random time. 12 | 13 | jobs: 14 | test: 15 | uses: wp-cli/.github/.github/workflows/reusable-testing.yml@main 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | wp-cli.local.yml 3 | node_modules/ 4 | vendor/ 5 | *.zip 6 | *.tar.gz 7 | *.swp 8 | *.txt 9 | *.log 10 | composer.lock 11 | phpunit.xml 12 | phpcs.xml 13 | .phpcs.xml 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | We appreciate you taking the initiative to contribute to this project. 5 | 6 | Contributing isn’t limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation. 7 | 8 | For a more thorough introduction, [check out WP-CLI's guide to contributing](https://make.wordpress.org/cli/handbook/contributing/). This package follows those policy and guidelines. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2011-2018 WP-CLI Development Group (https://github.com/wp-cli/scaffold-package-command/contributors) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wp-cli/scaffold-package-command 2 | =============================== 3 | 4 | Scaffolds WP-CLI commands with functional tests, full README.md, and more. 5 | 6 | [![Testing](https://github.com/wp-cli/scaffold-package-command/actions/workflows/testing.yml/badge.svg)](https://github.com/wp-cli/scaffold-package-command/actions/workflows/testing.yml) [![CircleCI](https://circleci.com/gh/wp-cli/scaffold-package-command/tree/main.svg?style=svg)](https://circleci.com/gh/wp-cli/scaffold-package-command/tree/main) 7 | 8 | Quick links: [Using](#using) | [Installing](#installing) | [Contributing](#contributing) | [Support](#support) 9 | 10 | ## Using 11 | 12 | This package implements the following commands: 13 | 14 | ### wp scaffold package 15 | 16 | Generate the files needed for a basic WP-CLI command. 17 | 18 | ~~~ 19 | wp scaffold package [--description=] [--homepage=] [--dir=] [--license=] [--require_wp_cli=] [--require_wp_cli_tests=] [--skip-tests] [--skip-readme] [--skip-github] [--skip-install] [--force] 20 | ~~~ 21 | 22 | Default behavior is to create the following files: 23 | - command.php 24 | - composer.json (with package name, description, and license) 25 | - .gitignore, .editorconfig, and .distignore 26 | - README.md (via wp scaffold package-readme) 27 | - Test harness (via wp scaffold package-tests) 28 | 29 | Unless specified with `--dir=`, the command package is placed in the 30 | WP-CLI `packages/local/` directory. 31 | 32 | **OPTIONS** 33 | 34 | 35 | Name for the new package. Expects / (e.g. 'wp-cli/scaffold-package'). 36 | 37 | [--description=] 38 | Human-readable description for the package. 39 | 40 | [--homepage=] 41 | Homepage for the package. Defaults to 'https://github.com/' 42 | 43 | [--dir=] 44 | Specify a destination directory for the command. Defaults to WP-CLI's `packages/local/` directory. 45 | 46 | [--license=] 47 | License for the package. 48 | --- 49 | default: MIT 50 | --- 51 | 52 | [--require_wp_cli=] 53 | Required WP-CLI version for the package. 54 | --- 55 | default: ^2.11 56 | --- 57 | 58 | [--require_wp_cli_tests=] 59 | Required WP-CLI testing framework version for the package. 60 | --- 61 | default: ^4.3.9 62 | --- 63 | 64 | [--skip-tests] 65 | Don't generate files for integration testing. 66 | 67 | [--skip-readme] 68 | Don't generate a README.md for the package. 69 | 70 | [--skip-github] 71 | Don't generate GitHub issue and pull request templates. 72 | 73 | [--skip-install] 74 | Don't install the package after scaffolding. 75 | 76 | [--force] 77 | Overwrite files that already exist. 78 | 79 | 80 | 81 | ### wp scaffold package-tests 82 | 83 | Generate files for writing Behat tests for your command. 84 | 85 | ~~~ 86 | wp scaffold package-tests [--ci=] [--force] 87 | ~~~ 88 | 89 | WP-CLI makes use of a Behat-based testing framework, which you should use 90 | too. This command generates all of the files you need. Functional tests 91 | are an integral ingredient of high-quality, maintainable commands. 92 | Behat is a great choice as a testing framework because: 93 | 94 | * It’s easy to write new tests, which means they’ll actually get written. 95 | * The tests interface with your command in the same manner as your users 96 | interface with your command, and they describe how the command is 97 | expected to work in human-readable terms. 98 | 99 | Behat tests live in the `features/` directory of your project. When you 100 | use this command, it will generate a default test that looks like this: 101 | 102 | ``` 103 | Feature: Test that WP-CLI loads. 104 | 105 | Scenario: WP-CLI loads for your tests 106 | Given a WP install 107 | 108 | When I run `wp eval 'echo "Hello world.";'` 109 | Then STDOUT should contain: 110 | """ 111 | Hello world. 112 | """ 113 | ``` 114 | 115 | Functional tests typically follow this pattern: 116 | 117 | * **Given** some background, 118 | * **When** a user performs a specific action, 119 | * **Then** the end result should be X (and Y and Z). 120 | 121 | View all defined Behat steps available for use with `behat -dl`: 122 | 123 | ``` 124 | Given /^an empty directory$/ 125 | Given /^an empty cache/ 126 | Given /^an? ([^\s]+) file:$/ 127 | Given /^"([^"]+)" replaced with "([^"]+)" in the ([^\s]+) file$/ 128 | ``` 129 | 130 | The files generated by this command include: 131 | 132 | * `.travis.yml` is the configuration file for Travis CI. 133 | * `bin/install-package-tests.sh` will configure your environment to run 134 | the tests. 135 | * `bin/test.sh` is a test runner that respects contextual Behat tags. 136 | * `features/load-wp-cli.feature` is a basic test to confirm WP-CLI can 137 | load. 138 | * `features/bootstrap`, `features/steps`, `features/extra` are Behat 139 | configuration files. 140 | 141 | After running `bin/install-package-tests.sh`, you can run the tests with 142 | `./vendor/bin/behat`. If you find yourself using Behat on a number of 143 | projects and don't want to install a copy with each one, you can 144 | `composer global require behat/behat` to install Behat globally on your 145 | machine. Make sure `~/.composer/vendor/bin` has also been added to your 146 | `$PATH`. Once you've done so, you can run the tests for a project by 147 | calling `behat`. 148 | 149 | For Travis CI, specially-named files in the package directory can be 150 | used to modify the generated `.travis.yml`, where `` is one of 151 | 'cache', 'env', 'matrix', 'before_install', 'install', 'before_script', 'script': 152 | * `travis-.yml` - contents used for `:` (if present following ignored) 153 | * `travis--append.yml` - contents appended to generated `:` 154 | 155 | You can also append to the generated `.travis.yml` with the file: 156 | * `travis-append.yml` - contents appended to generated `.travis.yml` 157 | 158 | **ENVIRONMENT** 159 | 160 | The `features/bootstrap/FeatureContext.php` file expects the 161 | WP_CLI_BIN_DIR environment variable. 162 | 163 | WP-CLI Behat framework uses Behat ~2.5, which is installed with Composer. 164 | 165 | **OPTIONS** 166 | 167 | 168 | Directory path to an existing package to generate tests for. 169 | 170 | [--ci=] 171 | Create a configuration file for a specific CI provider. 172 | --- 173 | default: travis 174 | options: 175 | - travis 176 | - circle 177 | - github 178 | --- 179 | 180 | [--force] 181 | Overwrite files that already exist. 182 | 183 | **EXAMPLES** 184 | 185 | # Generate files for writing Behat tests. 186 | $ wp scaffold package-tests /path/to/command/dir/ 187 | Success: Created package test files. 188 | 189 | 190 | 191 | ### wp scaffold package-readme 192 | 193 | Generate a README.md for your command. 194 | 195 | ~~~ 196 | wp scaffold package-readme [--force] [--branch=] 197 | ~~~ 198 | 199 | Creates a README.md with Using, Installing, and Contributing instructions 200 | based on the composer.json file for your WP-CLI package. Run this command 201 | at the beginning of your project, and then every time your usage docs 202 | change. 203 | 204 | These command-specific docs are generated based composer.json -> 'extra' 205 | -> 'commands'. For instance, this package's composer.json includes: 206 | 207 | ``` 208 | { 209 | "name": "wp-cli/scaffold-package-command", 210 | // [...] 211 | "extra": { 212 | "commands": [ 213 | "scaffold package", 214 | "scaffold package-tests", 215 | "scaffold package-readme" 216 | ] 217 | } 218 | } 219 | ``` 220 | 221 | You can also customize the rendering of README.md generally with 222 | composer.json -> 'extra' -> 'readme'. For example, runcommand/hook's 223 | composer.json includes: 224 | 225 | ``` 226 | { 227 | "extra": { 228 | "commands": [ 229 | "hook" 230 | ], 231 | "readme": { 232 | "shields": [ 233 | "[![Build Status](https://travis-ci.org/runcommand/reset-password.svg?branch=master)](https://travis-ci.org/runcommand/reset-password)" 234 | ], 235 | "sections": [ 236 | "Using", 237 | "Installing", 238 | "Support" 239 | ], 240 | "support": { 241 | "body": "https://raw.githubusercontent.com/runcommand/runcommand-theme/master/bin/readme-partials/support-open-source.md" 242 | }, 243 | "show_powered_by": false 244 | } 245 | } 246 | } 247 | ``` 248 | 249 | In this example: 250 | 251 | * "shields" supports arbitrary images as shields to display. 252 | * "sections" permits defining arbitrary sections (instead of default Using, Installing and Contributing). 253 | * "support" -> "body" uses a remote Markdown file as the section contents. This can also be a local file path, or a string. 254 | * "show_powered_by" shows or hides the Powered By mention at the end of the readme. 255 | 256 | For sections, "pre", "body" and "post" are supported. Example: 257 | ``` 258 | "support": { 259 | "pre": "highlight.md", 260 | "body": "https://raw.githubusercontent.com/runcommand/runcommand-theme/master/bin/readme-partials/support-open-source.md", 261 | "post": "This is additional text to show after main body content." 262 | }, 263 | ``` 264 | In this example: 265 | 266 | * "pre" content is pulled from local highlight.md file. 267 | * "body" content is pulled from remote URL. 268 | * "post" is a string. 269 | 270 | **OPTIONS** 271 | 272 | 273 | Directory path to an existing package to generate a readme for. 274 | 275 | [--force] 276 | Overwrite the readme if it already exists. 277 | 278 | [--branch=] 279 | Name of default branch of the underlying repository. Defaults to master. 280 | 281 | 282 | 283 | ### wp scaffold package-github 284 | 285 | Generate GitHub configuration files for your command. 286 | 287 | ~~~ 288 | wp scaffold package-github [--force] 289 | ~~~ 290 | 291 | Creates a variety of files to better manage your project on GitHub. These 292 | files include: 293 | 294 | * `.github/ISSUE_TEMPLATE` - Text displayed when a user opens a new issue. 295 | * `.github/PULL_REQUEST_TEMPLATE` - Text displayed when a user submits a pull request. 296 | * `.github/settings.yml` - Configuration file for the [Probot settings app](https://probot.github.io/apps/settings/). 297 | 298 | **OPTIONS** 299 | 300 | 301 | Directory path to an existing package to generate GitHub configuration for. 302 | 303 | [--force] 304 | Overwrite files that already exist. 305 | 306 | ## Installing 307 | 308 | Installing this package requires WP-CLI v2.12 or greater. Update to the latest stable release with `wp cli update`. 309 | 310 | Once you've done so, you can install the latest stable version of this package with: 311 | 312 | ```bash 313 | wp package install wp-cli/scaffold-package-command:@stable 314 | ``` 315 | 316 | To install the latest development version of this package, use the following command instead: 317 | 318 | ```bash 319 | wp package install wp-cli/scaffold-package-command:dev-main 320 | ``` 321 | 322 | ## Contributing 323 | 324 | We appreciate you taking the initiative to contribute to this project. 325 | 326 | Contributing isn’t limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation. 327 | 328 | For a more thorough introduction, [check out WP-CLI's guide to contributing](https://make.wordpress.org/cli/handbook/contributing/). This package follows those policy and guidelines. 329 | 330 | ### Reporting a bug 331 | 332 | Think you’ve found a bug? We’d love for you to help us get it fixed. 333 | 334 | Before you create a new issue, you should [search existing issues](https://github.com/wp-cli/scaffold-package-command/issues?q=label%3Abug%20) to see if there’s an existing resolution to it, or if it’s already been fixed in a newer version. 335 | 336 | Once you’ve done a bit of searching and discovered there isn’t an open or fixed issue for your bug, please [create a new issue](https://github.com/wp-cli/scaffold-package-command/issues/new). Include as much detail as you can, and clear steps to reproduce if possible. For more guidance, [review our bug report documentation](https://make.wordpress.org/cli/handbook/bug-reports/). 337 | 338 | ### Creating a pull request 339 | 340 | Want to contribute a new feature? Please first [open a new issue](https://github.com/wp-cli/scaffold-package-command/issues/new) to discuss whether the feature is a good fit for the project. 341 | 342 | Once you've decided to commit the time to seeing your pull request through, [please follow our guidelines for creating a pull request](https://make.wordpress.org/cli/handbook/pull-requests/) to make sure it's a pleasant experience. See "[Setting up](https://make.wordpress.org/cli/handbook/pull-requests/#setting-up)" for details specific to working on this package locally. 343 | 344 | ## Support 345 | 346 | GitHub issues aren't for general support questions, but there are other venues you can try: https://wp-cli.org/#support 347 | 348 | 349 | *This README.md is generated dynamically from the project's codebase using `wp scaffold package-readme` ([doc](https://github.com/wp-cli/scaffold-package-command#wp-scaffold-package-readme)). To suggest changes, please submit a pull request against the corresponding part of the codebase.* 350 | -------------------------------------------------------------------------------- /behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | default: 4 | contexts: 5 | - WP_CLI\Tests\Context\FeatureContext 6 | paths: 7 | - features 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wp-cli/scaffold-package-command", 3 | "description": "Scaffolds WP-CLI commands with functional tests, full README.md, and more.", 4 | "type": "wp-cli-package", 5 | "homepage": "https://github.com/wp-cli/scaffold-package-command", 6 | "support": { 7 | "issues": "https://github.com/wp-cli/scaffold-package-command/issues" 8 | }, 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Daniel Bachhuber", 13 | "email": "daniel@runcommand.io", 14 | "homepage": "https://runcommand.io" 15 | } 16 | ], 17 | "minimum-stability": "dev", 18 | "prefer-stable": true, 19 | "autoload": { 20 | "psr-4": { 21 | "WP_CLI\\": "src" 22 | }, 23 | "files": [ 24 | "scaffold-package-command.php" 25 | ] 26 | }, 27 | "require": { 28 | "php": ">=7.2.24", 29 | "wp-cli/package-command": "^2", 30 | "wp-cli/scaffold-command": "^2", 31 | "wp-cli/wp-cli": "^2.12" 32 | }, 33 | "require-dev": { 34 | "wp-cli/wp-cli-tests": "^4" 35 | }, 36 | "config": { 37 | "process-timeout": 7200, 38 | "sort-packages": true, 39 | "allow-plugins": { 40 | "dealerdirect/phpcodesniffer-composer-installer": true, 41 | "johnpbloch/wordpress-core-installer": true 42 | }, 43 | "lock": false 44 | }, 45 | "extra": { 46 | "branch-alias": { 47 | "dev-main": "2.x-dev" 48 | }, 49 | "commands": [ 50 | "scaffold package", 51 | "scaffold package-tests", 52 | "scaffold package-readme", 53 | "scaffold package-github" 54 | ] 55 | }, 56 | "scripts": { 57 | "behat": "run-behat-tests", 58 | "behat-rerun": "rerun-behat-tests", 59 | "lint": "run-linter-tests", 60 | "phpcs": "run-phpcs-tests", 61 | "phpcbf": "run-phpcbf-cleanup", 62 | "phpunit": "run-php-unit-tests", 63 | "prepare-tests": "install-package-tests", 64 | "test": [ 65 | "@lint", 66 | "@phpcs", 67 | "@phpunit", 68 | "@behat" 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /features/scaffold-package-github.feature: -------------------------------------------------------------------------------- 1 | Feature: Scaffold GitHub configuration for an existing package 2 | 3 | Scenario: Fails when invalid directory provided 4 | Given an empty directory 5 | 6 | When I try `wp scaffold package-github bar` 7 | Then the bar directory should not exist 8 | And STDERR should be: 9 | """ 10 | Error: Directory does not exist. 11 | """ 12 | And the return code should be 1 13 | 14 | Scenario: Fails when invalid package provided 15 | Given an empty directory 16 | And a baz/empty file: 17 | """ 18 | """ 19 | 20 | When I try `wp scaffold package-github baz` 21 | Then the baz directory should exist 22 | But the baz/.github directory should not exist 23 | And STDERR should be: 24 | """ 25 | Error: Invalid package directory. composer.json file must be present. 26 | """ 27 | And the return code should be 1 28 | 29 | Scenario: Scaffold GitHub configuration based on defaults 30 | Given an empty directory 31 | 32 | When I run `wp package path` 33 | Then save STDOUT as {PACKAGE_PATH} 34 | 35 | When I run `wp scaffold package wp-cli/default-github --skip-github` 36 | Then the {PACKAGE_PATH}/local/wp-cli/default-github directory should exist 37 | 38 | When I run `wp scaffold package-github {PACKAGE_PATH}/local/wp-cli/default-github` 39 | Then STDOUT should contain: 40 | """ 41 | Success: Created package GitHub configuration. 42 | """ 43 | And the {PACKAGE_PATH}/local/wp-cli/default-github/.github directory should exist 44 | And the {PACKAGE_PATH}/local/wp-cli/default-github/.github/ISSUE_TEMPLATE file should contain: 45 | """ 46 | Thanks for creating a new issue! 47 | """ 48 | And the {PACKAGE_PATH}/local/wp-cli/default-github/.github/PULL_REQUEST_TEMPLATE file should contain: 49 | """ 50 | Thanks for submitting a pull request! 51 | """ 52 | -------------------------------------------------------------------------------- /features/scaffold-package-readme.feature: -------------------------------------------------------------------------------- 1 | Feature: Scaffold a README.md file for an existing package 2 | 3 | Scenario: Fails when invalid directory provided 4 | Given an empty directory 5 | 6 | When I try `wp scaffold package-readme bar` 7 | Then the bar directory should not exist 8 | And STDERR should be: 9 | """ 10 | Error: Directory does not exist. 11 | """ 12 | And the return code should be 1 13 | 14 | Scenario: Fails when invalid package provided 15 | Given an empty directory 16 | And a baz/empty file: 17 | """ 18 | """ 19 | 20 | When I try `wp scaffold package-readme baz` 21 | Then the baz directory should exist 22 | But the baz/README.md file should not exist 23 | And STDERR should be: 24 | """ 25 | Error: Invalid package directory. composer.json file must be present. 26 | """ 27 | And the return code should be 1 28 | 29 | Scenario: Scaffold a README.md based on the defaults 30 | Given an empty directory 31 | 32 | When I run `wp package path` 33 | Then save STDOUT as {PACKAGE_PATH} 34 | 35 | When I run `wp scaffold package wp-cli/default-readme` 36 | Then STDOUT should contain: 37 | """ 38 | Success: Created package readme. 39 | """ 40 | And the {PACKAGE_PATH}/local/wp-cli/default-readme/README.md file should exist 41 | And the {PACKAGE_PATH}/local/wp-cli/default-readme/README.md file should contain: 42 | """ 43 | Installing this package requires WP-CLI v2.11 or greater. Update to the latest stable release with `wp cli update`. 44 | """ 45 | And the {PACKAGE_PATH}/local/wp-cli/default-readme/README.md file should contain: 46 | """ 47 | [![Build Status](https://travis-ci.org/wp-cli/default-readme.svg?branch=master) 48 | """ 49 | And the {PACKAGE_PATH}/local/wp-cli/default-readme/README.md file should contain: 50 | """ 51 | *This README.md is generated dynamically from the project's codebase 52 | """ 53 | And the {PACKAGE_PATH}/local/wp-cli/default-readme/README.md file should contain: 54 | """ 55 | wp package install wp-cli/default-readme:dev-master 56 | """ 57 | When I run `wp package uninstall wp-cli/default-readme` 58 | Then STDOUT should contain: 59 | """ 60 | Success: Uninstalled package. 61 | """ 62 | 63 | Scenario: Scaffold a README.md based with custom repository branch 64 | Given an empty directory 65 | 66 | When I run `wp package path` 67 | Then save STDOUT as {PACKAGE_PATH} 68 | 69 | When I run `wp scaffold package wp-cli/custom-branch` 70 | Then STDOUT should contain: 71 | """ 72 | Success: Created package readme. 73 | """ 74 | # `wp scaffold package-readme --force` returns a warning 75 | And I try `wp scaffold package-readme {PACKAGE_PATH}/local/wp-cli/custom-branch --branch=custom --force` 76 | And the {PACKAGE_PATH}/local/wp-cli/custom-branch/README.md file should exist 77 | And the {PACKAGE_PATH}/local/wp-cli/custom-branch/README.md file should contain: 78 | """ 79 | Installing this package requires WP-CLI v2.11 or greater. Update to the latest stable release with `wp cli update`. 80 | """ 81 | And the {PACKAGE_PATH}/local/wp-cli/custom-branch/README.md file should contain: 82 | """ 83 | [![Build Status](https://travis-ci.org/wp-cli/custom-branch.svg?branch=custom) 84 | """ 85 | And the {PACKAGE_PATH}/local/wp-cli/custom-branch/README.md file should contain: 86 | """ 87 | *This README.md is generated dynamically from the project's codebase 88 | """ 89 | 90 | Scenario: Scaffold a README.md requiring a nightly build 91 | Given an empty directory 92 | 93 | When I run `wp scaffold package wp-cli/foo --dir=foo --require_wp_cli='>=0.24.0-alpha'` 94 | Then STDOUT should contain: 95 | """ 96 | Success: Created package readme. 97 | """ 98 | And the foo/composer.json file should contain: 99 | """ 100 | "require": { 101 | "wp-cli/wp-cli": ">=0.24.0-alpha" 102 | }, 103 | """ 104 | And the foo/README.md file should exist 105 | And the foo/README.md file should contain: 106 | """ 107 | Installing this package requires WP-CLI v0.24.0-alpha or greater. Update to the latest nightly release with `wp cli update --nightly`. 108 | """ 109 | When I run `wp package uninstall wp-cli/foo` 110 | Then STDOUT should contain: 111 | """ 112 | Success: Uninstalled package. 113 | """ 114 | 115 | Scenario: Scaffold a README.md requiring the latest stable release 116 | Given an empty directory 117 | 118 | When I run `wp scaffold package wp-cli/foo --dir=foo --require_wp_cli='*'` 119 | Then STDOUT should contain: 120 | """ 121 | Success: Created package readme. 122 | """ 123 | And the foo/composer.json file should contain: 124 | """ 125 | "require": { 126 | "wp-cli/wp-cli": "*" 127 | }, 128 | """ 129 | And the foo/README.md file should exist 130 | And the foo/README.md file should contain: 131 | """ 132 | Installing this package requires WP-CLI's latest stable release. Update to the latest stable release with `wp cli update`. 133 | """ 134 | When I run `wp package uninstall wp-cli/foo` 135 | Then STDOUT should contain: 136 | """ 137 | Success: Uninstalled package. 138 | """ 139 | 140 | Scenario: Scaffold a readme with custom shields 141 | Given an empty directory 142 | And a foo/composer.json file: 143 | """ 144 | { 145 | "name": "runcommand/profile", 146 | "description": "Quickly identify what's slow with WordPress.", 147 | "homepage": "https://runcommand.io/wp/profile/", 148 | "license": "GPL-2.0", 149 | "authors": [], 150 | "minimum-stability": "dev", 151 | "autoload": { 152 | "files": [ "command.php" ] 153 | }, 154 | "require": { 155 | "wp-cli/wp-cli": "^2.11" 156 | }, 157 | "require-dev": { 158 | "wp-cli/wp-cli-tests": "^4.3.9" 159 | }, 160 | "extra": { 161 | "readme": { 162 | "shields": [ 163 | "shield 1", 164 | "shield 2", 165 | "shield 3" 166 | ] 167 | } 168 | } 169 | } 170 | """ 171 | 172 | When I run `wp scaffold package-readme foo` 173 | Then the foo/README.md file should exist 174 | And the foo/README.md file should contain: 175 | """ 176 | shield 1 shield 2 shield 3 177 | """ 178 | 179 | Scenario: Scaffold a readme with a remote support body 180 | Given an empty directory 181 | And a foo/composer.json file: 182 | """ 183 | { 184 | "name": "runcommand/profile", 185 | "description": "Quickly identify what's slow with WordPress.", 186 | "homepage": "https://runcommand.io/wp/profile/", 187 | "extra": { 188 | "readme": { 189 | "contributing": { 190 | "body": "https://gist.githubusercontent.com/danielbachhuber/bb652b1b744cea541705ee9c13605dad/raw/195c17ebb8cf25e947a9df6e02de1e96a084c287/support.md" 191 | } 192 | } 193 | } 194 | } 195 | """ 196 | 197 | When I run `wp scaffold package-readme foo` 198 | Then the foo/README.md file should exist 199 | And the foo/README.md file should contain: 200 | """ 201 | ## Contributing 202 | 203 | Support isn't free! 204 | """ 205 | 206 | Scenario: Scaffold a readme with a pre, post and body for the section 207 | Given an empty directory 208 | And a foo/composer.json file: 209 | """ 210 | { 211 | "name": "runcommand/profile", 212 | "description": "Quickly identify what's slow with WordPress.", 213 | "homepage": "https://runcommand.io/wp/profile/", 214 | "extra": { 215 | "readme": { 216 | "contributing": { 217 | "pre": "[Visit Site](https://example.com)", 218 | "body": "https://gist.githubusercontent.com/danielbachhuber/bb652b1b744cea541705ee9c13605dad/raw/195c17ebb8cf25e947a9df6e02de1e96a084c287/support.md", 219 | "post": "I am after body." 220 | } 221 | } 222 | } 223 | } 224 | """ 225 | 226 | When I run `wp scaffold package-readme foo` 227 | Then the foo/README.md file should exist 228 | And the foo/README.md file should contain: 229 | """ 230 | ## Contributing 231 | 232 | [Visit Site](https://example.com) 233 | 234 | Support isn't free! 235 | 236 | I am after body. 237 | """ 238 | 239 | Scenario: Scaffold a readme with custom sections 240 | Given an empty directory 241 | And a foo/composer.json file: 242 | """ 243 | { 244 | "name": "runcommand/profile", 245 | "description": "Quickly identify what's slow with WordPress.", 246 | "homepage": "https://runcommand.io/wp/profile/", 247 | "extra": { 248 | "readme": { 249 | "sections": [ 250 | "Installing", 251 | "Donating" 252 | ], 253 | "donating": { 254 | "body": "Give me money!" 255 | } 256 | } 257 | } 258 | } 259 | """ 260 | 261 | When I run `wp scaffold package-readme foo` 262 | Then the foo/README.md file should exist 263 | And the foo/README.md file should contain: 264 | """ 265 | Quick links: [Installing](#installing) | [Donating](#donating) 266 | """ 267 | And the foo/README.md file should contain: 268 | """ 269 | ## Donating 270 | 271 | Give me money! 272 | """ 273 | 274 | Scenario: Scaffold a readme without the powered by 275 | Given an empty directory 276 | And a foo/composer.json file: 277 | """ 278 | { 279 | "name": "runcommand/profile", 280 | "description": "Quickly identify what's slow with WordPress.", 281 | "homepage": "https://runcommand.io/wp/profile/", 282 | "extra": { 283 | "readme": { 284 | "show_powered_by": false 285 | } 286 | } 287 | } 288 | """ 289 | 290 | When I run `wp scaffold package-readme foo` 291 | Then the foo/README.md file should exist 292 | And the foo/README.md file should not contain: 293 | """ 294 | *This README.md is generated dynamically from the project's codebase 295 | """ 296 | 297 | @broken 298 | Scenario: Error when commands are specified but not present 299 | Given an empty directory 300 | And a foo/composer.json file: 301 | """ 302 | { 303 | "name": "runcommand/profile", 304 | "description": "Quickly identify what's slow with WordPress.", 305 | "homepage": "https://runcommand.io/wp/profile/", 306 | "extra": { 307 | "commands": [ 308 | "profile" 309 | ] 310 | } 311 | } 312 | """ 313 | 314 | When I try `wp scaffold package-readme foo` 315 | Then STDERR should be: 316 | """ 317 | Error: Missing one or more commands defined in composer.json -> extra -> commands. 318 | """ 319 | And the return code should be 1 320 | 321 | Scenario: README for a bundled command 322 | Given an empty directory 323 | And a foo/composer.json file: 324 | """ 325 | { 326 | "name": "runcommand/profile", 327 | "authors": [], 328 | "minimum-stability": "dev", 329 | "autoload": { 330 | "files": [ "command.php" ] 331 | }, 332 | "require": { 333 | }, 334 | "require-dev": { 335 | "wp-cli/wp-cli": "*", 336 | "wp-cli/wp-cli-tests": "^4.3.9" 337 | }, 338 | "extra": { 339 | "bundled": true 340 | } 341 | } 342 | """ 343 | 344 | When I run `wp scaffold package-readme foo` 345 | Then the foo/README.md file should exist 346 | And the foo/README.md file should contain: 347 | """ 348 | runcommand/profile 349 | ================== 350 | """ 351 | And the foo/README.md file should contain: 352 | """ 353 | This package is included with WP-CLI itself 354 | """ 355 | -------------------------------------------------------------------------------- /features/scaffold-package-tests.feature: -------------------------------------------------------------------------------- 1 | Feature: Scaffold the test suite for an existing package 2 | 3 | Background: 4 | Given a WP install 5 | And a community-command/command.php file: 6 | """ 7 | [-append]].yml append/override files. 130 | Given a community-command/travis-cache-append.yml file: 131 | """ 132 | - $HOME/my-append-cache 133 | """ 134 | And a community-command/travis-env-append.yml file: 135 | """ 136 | - MY_APPEND_ENV="my-append-env" 137 | """ 138 | And a community-command/travis-jobs-append.yml file: 139 | """ 140 | - stage: test 141 | php: 99.97 142 | env: WP_VERSION=9997.9997 143 | """ 144 | And a community-command/travis-before_install-append.yml file: 145 | """ 146 | - bash bin/my-append-before_install.sh 147 | - php -m 148 | """ 149 | And a community-command/travis-install-append.yml file: 150 | """ 151 | - bash bin/my-append-install.sh 152 | """ 153 | And a community-command/travis-script-append.yml file: 154 | """ 155 | - bash bin/my-append-script.sh 156 | """ 157 | And a community-command/travis-append.yml file: 158 | """ 159 | 160 | addons: 161 | apt: 162 | packages: 163 | - ghostscript 164 | """ 165 | 166 | When I run `wp scaffold package-tests community-command` 167 | Then STDOUT should not be empty 168 | And the community-command/.travis.yml file should exist 169 | And the community-command/.travis.yml file should contain: 170 | """ 171 | - $HOME/.composer/cache 172 | """ 173 | And the community-command/.travis.yml file should contain: 174 | """ 175 | - $HOME/my-append-cache 176 | 177 | env: 178 | """ 179 | And the community-command/.travis.yml file should contain: 180 | """ 181 | - PATH="$TRAVIS_BUILD_DIR/vendor/bin:$PATH" 182 | """ 183 | And the community-command/.travis.yml file should contain: 184 | """ 185 | - MY_APPEND_ENV="my-append-env" 186 | 187 | before_install: 188 | """ 189 | And the community-command/.travis.yml file should contain: 190 | """ 191 | php: 7.4 192 | """ 193 | And the community-command/.travis.yml file should contain: 194 | """ 195 | - stage: test 196 | php: 99.97 197 | env: WP_VERSION=9997.9997 198 | 199 | before_install: 200 | """ 201 | And the community-command/.travis.yml file should contain: 202 | """ 203 | # Remove Xdebug 204 | """ 205 | And the community-command/.travis.yml file should contain: 206 | """ 207 | - bash bin/my-append-before_install.sh 208 | - php -m 209 | 210 | install: 211 | """ 212 | And the community-command/.travis.yml file should contain: 213 | """ 214 | - composer prepare-tests 215 | """ 216 | And the community-command/.travis.yml file should contain: 217 | """ 218 | - bash bin/my-append-install.sh 219 | 220 | script: 221 | """ 222 | And the community-command/.travis.yml file should contain: 223 | """ 224 | - composer validate 225 | """ 226 | And the community-command/.travis.yml file should contain: 227 | """ 228 | - composer behat || composer behat-rerun 229 | - bash bin/my-append-script.sh 230 | """ 231 | And the community-command/.travis.yml file should contain: 232 | """ 233 | 234 | addons: 235 | apt: 236 | packages: 237 | - ghostscript 238 | """ 239 | 240 | Given a community-command/travis-cache.yml file: 241 | """ 242 | cache: 243 | directories: 244 | - $HOME/my-overwrite-cache 245 | """ 246 | And a community-command/travis-env.yml file: 247 | """ 248 | env: 249 | global: 250 | - MY_OVERWRITE_ENV="my-overwrite-env" 251 | """ 252 | And a community-command/travis-matrix.yml file: 253 | """ 254 | matrix: 255 | include: 256 | - php: 99.99 257 | env: WP_VERSION=9999.9999 258 | - php: 99.98 259 | env: WP_VERSION=9999.9998 260 | """ 261 | And a community-command/travis-before_install.yml file: 262 | """ 263 | before_install: 264 | - bash bin/my-overwrite-before_install.sh 265 | """ 266 | And a community-command/travis-install.yml file: 267 | """ 268 | install: 269 | - bash bin/my-overwrite-install.sh 270 | - bash bin/my-overwrite-install2.sh 271 | """ 272 | And a community-command/travis-before_script.yml file: 273 | """ 274 | before_script: 275 | - bash bin/my-overwrite-before_script.sh 276 | """ 277 | And a community-command/travis-script.yml file: 278 | """ 279 | script: 280 | - bash bin/my-overwrite-script.sh 281 | """ 282 | 283 | When I try `wp scaffold package-tests community-command --force` 284 | Then STDOUT should not be empty 285 | And the community-command/.travis.yml file should exist 286 | And the community-command/.travis.yml file should contain: 287 | """ 288 | cache: 289 | directories: 290 | - $HOME/my-overwrite-cache 291 | 292 | env: 293 | global: 294 | - MY_OVERWRITE_ENV="my-overwrite-env" 295 | 296 | matrix: 297 | include: 298 | - php: 99.99 299 | env: WP_VERSION=9999.9999 300 | - php: 99.98 301 | env: WP_VERSION=9999.9998 302 | 303 | before_install: 304 | - bash bin/my-overwrite-before_install.sh 305 | 306 | install: 307 | - bash bin/my-overwrite-install.sh 308 | - bash bin/my-overwrite-install2.sh 309 | 310 | before_script: 311 | - bash bin/my-overwrite-before_script.sh 312 | 313 | script: 314 | - bash bin/my-overwrite-script.sh 315 | """ 316 | And the community-command/.travis.yml file should contain: 317 | """ 318 | 319 | addons: 320 | apt: 321 | packages: 322 | - ghostscript 323 | """ 324 | # `travis-matrix.yml` overrides `travis-matrix-append.yml`. 325 | And the community-command/.travis.yml file should not contain: 326 | """ 327 | 9997 328 | """ 329 | # `travis-.yml` overrides `travis--append.yml`. 330 | And the community-command/.travis.yml file should not contain: 331 | """ 332 | my-append 333 | """ 334 | # `travis-cache.yml` overrides standard generated content. 335 | And the community-command/.travis.yml file should not contain: 336 | """ 337 | .composer 338 | """ 339 | # `travis-env.yml` overrides standard generated content. 340 | And the community-command/.travis.yml file should not contain: 341 | """ 342 | WP_CLI_BIN_DIR 343 | """ 344 | # `travis-matrix.yml` overrides standard generated content. 345 | And the community-command/.travis.yml file should not contain: 346 | """ 347 | 7.2 348 | """ 349 | # `travis-before_install.yml` overrides standard generated content. 350 | And the community-command/.travis.yml file should not contain: 351 | """ 352 | # Remove Xdebug 353 | """ 354 | # `travis-install/before_script.yml` overrides standard generated content. 355 | And the community-command/.travis.yml file should not contain: 356 | """ 357 | composer 358 | """ 359 | # `travis-script.yml` overrides standard generated content. 360 | And the community-command/.travis.yml file should not contain: 361 | """ 362 | bin/test.sh 363 | """ 364 | And STDERR should contain: 365 | """ 366 | Warning: File already exists 367 | """ 368 | And the return code should be 0 369 | -------------------------------------------------------------------------------- /features/scaffold-package.feature: -------------------------------------------------------------------------------- 1 | Feature: Scaffold WP-CLI commands 2 | 3 | Scenario: Scaffold a WP-CLI command without tests 4 | Given an empty directory 5 | 6 | When I run `wp package path` 7 | Then save STDOUT as {PACKAGE_PATH} 8 | 9 | When I run `wp scaffold package wp-cli/foo --skip-tests` 10 | Then STDOUT should contain: 11 | """ 12 | Success: Created package files 13 | """ 14 | And the {PACKAGE_PATH}/local/wp-cli/foo/.gitignore file should exist 15 | And the {PACKAGE_PATH}/local/wp-cli/foo/.editorconfig file should exist 16 | And the {PACKAGE_PATH}/local/wp-cli/foo/.distignore file should exist 17 | And the {PACKAGE_PATH}/local/wp-cli/foo/.distignore file should contain: 18 | """ 19 | .gitignore 20 | """ 21 | And the {PACKAGE_PATH}/local/wp-cli/foo/composer.json file should exist 22 | And the {PACKAGE_PATH}/local/wp-cli/foo/composer.json file should contain: 23 | """ 24 | "type": "wp-cli-package", 25 | """ 26 | And the {PACKAGE_PATH}/local/wp-cli/foo/composer.json file should contain: 27 | """ 28 | "homepage": "https://github.com/wp-cli/foo", 29 | """ 30 | And the {PACKAGE_PATH}/local/wp-cli/foo/composer.json file should contain: 31 | """ 32 | "license": "MIT", 33 | """ 34 | And the {PACKAGE_PATH}/local/wp-cli/foo/composer.json file should contain: 35 | """ 36 | "require": { 37 | "wp-cli/wp-cli": "^2.11" 38 | }, 39 | """ 40 | And the {PACKAGE_PATH}/local/wp-cli/foo/hello-world-command.php file should exist 41 | And the {PACKAGE_PATH}/local/wp-cli/foo/CONTRIBUTING.md file should exist 42 | And the {PACKAGE_PATH}/local/wp-cli/foo/CONTRIBUTING.md file should contain: 43 | """ 44 | Contributing 45 | """ 46 | And the {PACKAGE_PATH}/local/wp-cli/foo/wp-cli.yml file should exist 47 | And the {PACKAGE_PATH}/local/wp-cli/foo/.travis.yml file should not exist 48 | And the {PACKAGE_PATH}/local/wp-cli/foo/.github/PULL_REQUEST_TEMPLATE file should exist 49 | And the {PACKAGE_PATH}/local/wp-cli/foo/.github/ISSUE_TEMPLATE file should exist 50 | 51 | When I run `wp hello-world` 52 | Then STDOUT should be: 53 | """ 54 | Success: Hello World! 55 | """ 56 | 57 | When I run `composer -q -n --working-dir={PACKAGE_PATH}/local/wp-cli/foo/ install` 58 | And I run `wp --require={PACKAGE_PATH}/local/wp-cli/foo/hello-world-command.php hello-world` 59 | Then STDOUT should be: 60 | """ 61 | Success: Hello World! 62 | """ 63 | 64 | When I run `cat {PACKAGE_PATH}/local/wp-cli/foo/wp-cli.yml` 65 | Then STDOUT should contain: 66 | """ 67 | require: 68 | - hello-world-command.php 69 | """ 70 | 71 | When I run `cat {PACKAGE_PATH}/local/wp-cli/foo/.gitignore` 72 | Then STDOUT should contain: 73 | """ 74 | .DS_Store 75 | """ 76 | 77 | When I run `cat {PACKAGE_PATH}/local/wp-cli/foo/.editorconfig` 78 | Then STDOUT should contain: 79 | """ 80 | This file is for unifying the coding style for different editors and IDEs 81 | """ 82 | 83 | When I run `wp package uninstall wp-cli/foo` 84 | Then STDOUT should contain: 85 | """ 86 | Success: Uninstalled package. 87 | """ 88 | 89 | Scenario: Scaffold a WP-CLI command without using --require 90 | Given an empty directory 91 | 92 | When I run `wp scaffold package wp-cli/without-require --skip-tests` 93 | Then STDOUT should contain: 94 | """ 95 | Success: Created package files 96 | """ 97 | 98 | When I run `wp hello-world` 99 | Then STDOUT should be: 100 | """ 101 | Success: Hello World! 102 | """ 103 | 104 | When I run `wp package uninstall wp-cli/without-require` 105 | Then STDOUT should contain: 106 | """ 107 | Success: Uninstalled package. 108 | """ 109 | 110 | Scenario: Scaffold a package with an invalid name 111 | Given an empty directory 112 | 113 | When I try `wp scaffold package foo` 114 | Then STDERR should be: 115 | """ 116 | Error: 'foo' is an invalid package name. Package scaffold expects '/'. 117 | """ 118 | And the return code should be 1 119 | 120 | Scenario: Scaffold a WP-CLI command to a custom directory 121 | Given an empty directory 122 | 123 | When I run `wp scaffold package wp-cli/custom-directory --dir=custom-directory --skip-tests` 124 | Then STDOUT should contain: 125 | """ 126 | Success: Created package files 127 | """ 128 | And the custom-directory/.gitignore file should exist 129 | And the custom-directory/.editorconfig file should exist 130 | And the custom-directory/composer.json file should exist 131 | And the custom-directory/hello-world-command.php file should exist 132 | And the custom-directory/wp-cli.yml file should exist 133 | And the custom-directory/.travis.yml file should not exist 134 | 135 | When I run `composer -q -n --working-dir=custom-directory/ install` 136 | And I run `wp --require=custom-directory/hello-world-command.php hello-world` 137 | Then STDOUT should be: 138 | """ 139 | Success: Hello World! 140 | """ 141 | When I run `wp package uninstall wp-cli/custom-directory` 142 | Then STDOUT should contain: 143 | """ 144 | Success: Uninstalled package. 145 | """ 146 | 147 | Scenario: Attempt to scaffold the same package twice 148 | Given an empty directory 149 | And a session file: 150 | """ 151 | s 152 | s 153 | s 154 | s 155 | s 156 | s 157 | s 158 | s 159 | s 160 | """ 161 | 162 | When I try `wp scaffold package wp-cli/same-package --skip-tests --skip-github` 163 | Then STDOUT should contain: 164 | """ 165 | Success: Created package files 166 | """ 167 | 168 | When I try `wp scaffold package wp-cli/same-package --skip-tests --skip-github < session` 169 | And STDERR should contain: 170 | """ 171 | Warning: File already exists 172 | """ 173 | Then STDOUT should contain: 174 | """ 175 | All package files were skipped 176 | """ 177 | And the return code should be 0 178 | 179 | When I try `wp package uninstall wp-cli/same-package` 180 | Then STDOUT should contain: 181 | """ 182 | Success: Uninstalled package. 183 | """ 184 | 185 | Scenario: Scaffold a WP-CLI command with tests 186 | Given an empty directory 187 | 188 | When I run `wp package path` 189 | Then save STDOUT as {PACKAGE_PATH} 190 | 191 | When I run `wp scaffold package wp-cli/with-tests` 192 | Then STDOUT should contain: 193 | """ 194 | Success: Created package files 195 | """ 196 | And the {PACKAGE_PATH}/local/wp-cli/with-tests/.gitignore file should exist 197 | And the {PACKAGE_PATH}/local/wp-cli/with-tests/.editorconfig file should exist 198 | And the {PACKAGE_PATH}/local/wp-cli/with-tests/composer.json file should exist 199 | And the {PACKAGE_PATH}/local/wp-cli/with-tests/hello-world-command.php file should exist 200 | And the {PACKAGE_PATH}/local/wp-cli/with-tests/wp-cli.yml file should exist 201 | And the {PACKAGE_PATH}/local/wp-cli/with-tests/.travis.yml file should exist 202 | 203 | When I run `composer -q -n --working-dir={PACKAGE_PATH}/local/wp-cli/with-tests/ install` 204 | And I run `wp --require={PACKAGE_PATH}/local/wp-cli/with-tests/hello-world-command.php hello-world` 205 | Then STDOUT should be: 206 | """ 207 | Success: Hello World! 208 | """ 209 | When I run `wp package uninstall wp-cli/with-tests` 210 | Then STDOUT should contain: 211 | """ 212 | Success: Uninstalled package. 213 | """ 214 | 215 | Scenario: Scaffold a command with a custom homepage 216 | Given an empty directory 217 | 218 | When I run `wp package path` 219 | Then save STDOUT as {PACKAGE_PATH} 220 | 221 | When I run `wp scaffold package wp-cli/bar --homepage='http://apple.com'` 222 | Then STDOUT should contain: 223 | """ 224 | Success: Created package files 225 | """ 226 | And the {PACKAGE_PATH}/local/wp-cli/bar/composer.json file should exist 227 | And the {PACKAGE_PATH}/local/wp-cli/bar/composer.json file should contain: 228 | """ 229 | "homepage": "http://apple.com", 230 | """ 231 | When I run `wp package uninstall wp-cli/bar` 232 | Then STDOUT should contain: 233 | """ 234 | Success: Uninstalled package. 235 | """ 236 | 237 | Scenario: Use tilde for HOME in package directory path 238 | Given an empty directory 239 | 240 | When I run `HOME={RUN_DIR} wp scaffold package bar/foo --dir=~/foo --force --skip-tests --skip-readme` 241 | Then STDOUT should contain: 242 | """ 243 | Success: Package installed. 244 | """ 245 | And the {RUN_DIR}/foo directory should exist 246 | 247 | Scenario: Scaffold a package but skip installation and GitHub templates 248 | Given an empty directory 249 | 250 | When I run `wp package path` 251 | Then save STDOUT as {PACKAGE_PATH} 252 | 253 | When I run `wp scaffold package wp-cli/foo --skip-install --skip-github` 254 | Then STDOUT should contain: 255 | """ 256 | Success: Created package files 257 | """ 258 | And STDOUT should not contain: 259 | """ 260 | Installing package 261 | """ 262 | And the {PACKAGE_PATH}/local/wp-cli/foo/.gitignore file should exist 263 | And the {PACKAGE_PATH}/local/wp-cli/foo/.github/PULL_REQUEST_TEMPLATE file should not exist 264 | And the {PACKAGE_PATH}/local/wp-cli/foo/.github/ISSUE_TEMPLATE file should not exist 265 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scaffold-package-command", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@protobufjs/aspromise": { 8 | "version": "1.1.2", 9 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 10 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", 11 | "dev": true 12 | }, 13 | "@protobufjs/base64": { 14 | "version": "1.1.2", 15 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 16 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", 17 | "dev": true 18 | }, 19 | "@protobufjs/codegen": { 20 | "version": "2.0.4", 21 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 22 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", 23 | "dev": true 24 | }, 25 | "@protobufjs/eventemitter": { 26 | "version": "1.1.0", 27 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 28 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", 29 | "dev": true 30 | }, 31 | "@protobufjs/fetch": { 32 | "version": "1.1.0", 33 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 34 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 35 | "dev": true, 36 | "requires": { 37 | "@protobufjs/aspromise": "^1.1.1", 38 | "@protobufjs/inquire": "^1.1.0" 39 | } 40 | }, 41 | "@protobufjs/float": { 42 | "version": "1.0.2", 43 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 44 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", 45 | "dev": true 46 | }, 47 | "@protobufjs/inquire": { 48 | "version": "1.1.0", 49 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 50 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", 51 | "dev": true 52 | }, 53 | "@protobufjs/path": { 54 | "version": "1.1.2", 55 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 56 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", 57 | "dev": true 58 | }, 59 | "@protobufjs/pool": { 60 | "version": "1.1.0", 61 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 62 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", 63 | "dev": true 64 | }, 65 | "@protobufjs/utf8": { 66 | "version": "1.1.0", 67 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 68 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", 69 | "dev": true 70 | }, 71 | "@types/long": { 72 | "version": "4.0.2", 73 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", 74 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", 75 | "dev": true 76 | }, 77 | "@types/node": { 78 | "version": "18.8.3", 79 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", 80 | "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", 81 | "dev": true 82 | }, 83 | "@types/uuid": { 84 | "version": "3.4.10", 85 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz", 86 | "integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==", 87 | "dev": true 88 | }, 89 | "balanced-match": { 90 | "version": "1.0.2", 91 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 92 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 93 | "dev": true 94 | }, 95 | "brace-expansion": { 96 | "version": "1.1.11", 97 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 98 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 99 | "dev": true, 100 | "requires": { 101 | "balanced-match": "^1.0.0", 102 | "concat-map": "0.0.1" 103 | } 104 | }, 105 | "buffer-from": { 106 | "version": "1.1.2", 107 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 108 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 109 | "dev": true 110 | }, 111 | "commander": { 112 | "version": "5.0.0", 113 | "resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz", 114 | "integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==", 115 | "dev": true 116 | }, 117 | "concat-map": { 118 | "version": "0.0.1", 119 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 120 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 121 | "dev": true 122 | }, 123 | "core-js": { 124 | "version": "3.6.4", 125 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", 126 | "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", 127 | "dev": true 128 | }, 129 | "cucumber-messages": { 130 | "version": "8.0.0", 131 | "resolved": "https://registry.npmjs.org/cucumber-messages/-/cucumber-messages-8.0.0.tgz", 132 | "integrity": "sha512-lUnWRMjwA9+KhDec/5xRZV3Du67ISumHnVLywWQXyvzmc4P+Eqx8CoeQrBQoau3Pw1hs4kJLTDyV85hFBF00SQ==", 133 | "dev": true, 134 | "requires": { 135 | "@types/uuid": "^3.4.6", 136 | "protobufjs": "^6.8.8", 137 | "uuid": "^3.3.3" 138 | } 139 | }, 140 | "fs.realpath": { 141 | "version": "1.0.0", 142 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 143 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 144 | "dev": true 145 | }, 146 | "gherkin": { 147 | "version": "9.0.0", 148 | "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-9.0.0.tgz", 149 | "integrity": "sha512-6xoAepoxo5vhkBXjB4RCfVnSKHu5z9SqXIQVUyj+Jw8BQX8odATlee5otXgdN8llZvyvHokuvNiBeB3naEnnIQ==", 150 | "dev": true, 151 | "requires": { 152 | "commander": "^4.0.1", 153 | "cucumber-messages": "8.0.0", 154 | "source-map-support": "^0.5.16" 155 | }, 156 | "dependencies": { 157 | "commander": { 158 | "version": "4.1.1", 159 | "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", 160 | "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", 161 | "dev": true 162 | } 163 | } 164 | }, 165 | "gherkin-lint": { 166 | "version": "4.2.2", 167 | "resolved": "https://registry.npmjs.org/gherkin-lint/-/gherkin-lint-4.2.2.tgz", 168 | "integrity": "sha512-+vu0wbrwxaaEdrheU9pH2MYR6zk38u2IkrCIg6IETUw1lkrNVAfIfOCihwrrL2NTJv5Iia/C7hZEBNwjGSkL2Q==", 169 | "dev": true, 170 | "requires": { 171 | "commander": "5.0.0", 172 | "core-js": "3.6.4", 173 | "gherkin": "9.0.0", 174 | "glob": "7.1.6", 175 | "lodash": "4.17.21", 176 | "strip-json-comments": "3.0.1", 177 | "xml-js": "^1.6.11" 178 | } 179 | }, 180 | "glob": { 181 | "version": "7.1.6", 182 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 183 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 184 | "dev": true, 185 | "requires": { 186 | "fs.realpath": "^1.0.0", 187 | "inflight": "^1.0.4", 188 | "inherits": "2", 189 | "minimatch": "^3.0.4", 190 | "once": "^1.3.0", 191 | "path-is-absolute": "^1.0.0" 192 | } 193 | }, 194 | "inflight": { 195 | "version": "1.0.6", 196 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 197 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 198 | "dev": true, 199 | "requires": { 200 | "once": "^1.3.0", 201 | "wrappy": "1" 202 | } 203 | }, 204 | "inherits": { 205 | "version": "2.0.4", 206 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 207 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 208 | "dev": true 209 | }, 210 | "lodash": { 211 | "version": "4.17.21", 212 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 213 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 214 | "dev": true 215 | }, 216 | "long": { 217 | "version": "4.0.0", 218 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", 219 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", 220 | "dev": true 221 | }, 222 | "minimatch": { 223 | "version": "3.1.2", 224 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 225 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 226 | "dev": true, 227 | "requires": { 228 | "brace-expansion": "^1.1.7" 229 | } 230 | }, 231 | "once": { 232 | "version": "1.4.0", 233 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 234 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 235 | "dev": true, 236 | "requires": { 237 | "wrappy": "1" 238 | } 239 | }, 240 | "path-is-absolute": { 241 | "version": "1.0.1", 242 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 243 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 244 | "dev": true 245 | }, 246 | "protobufjs": { 247 | "version": "6.11.4", 248 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", 249 | "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", 250 | "dev": true, 251 | "requires": { 252 | "@protobufjs/aspromise": "^1.1.2", 253 | "@protobufjs/base64": "^1.1.2", 254 | "@protobufjs/codegen": "^2.0.4", 255 | "@protobufjs/eventemitter": "^1.1.0", 256 | "@protobufjs/fetch": "^1.1.0", 257 | "@protobufjs/float": "^1.0.2", 258 | "@protobufjs/inquire": "^1.1.0", 259 | "@protobufjs/path": "^1.1.2", 260 | "@protobufjs/pool": "^1.1.0", 261 | "@protobufjs/utf8": "^1.1.0", 262 | "@types/long": "^4.0.1", 263 | "@types/node": ">=13.7.0", 264 | "long": "^4.0.0" 265 | } 266 | }, 267 | "sax": { 268 | "version": "1.2.4", 269 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 270 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", 271 | "dev": true 272 | }, 273 | "source-map": { 274 | "version": "0.6.1", 275 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 276 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 277 | "dev": true 278 | }, 279 | "source-map-support": { 280 | "version": "0.5.21", 281 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 282 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 283 | "dev": true, 284 | "requires": { 285 | "buffer-from": "^1.0.0", 286 | "source-map": "^0.6.0" 287 | } 288 | }, 289 | "strip-json-comments": { 290 | "version": "3.0.1", 291 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", 292 | "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", 293 | "dev": true 294 | }, 295 | "uuid": { 296 | "version": "3.4.0", 297 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 298 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 299 | "dev": true 300 | }, 301 | "wrappy": { 302 | "version": "1.0.2", 303 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 304 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 305 | "dev": true 306 | }, 307 | "xml-js": { 308 | "version": "1.6.11", 309 | "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", 310 | "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", 311 | "dev": true, 312 | "requires": { 313 | "sax": "^1.2.4" 314 | } 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scaffold-package-command", 3 | "version": "1.0.0", 4 | "description": "wp-cli/scaffold-package-command", 5 | "main": " ", 6 | "scripts": { 7 | "test": "echo \"Error: testing not setup\" && exit 1", 8 | "lint": "node ./node_modules/gherkin-lint/dist/main.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/wp-cli/scaffold-package-command.git" 13 | }, 14 | "author": "Daniel Bachhuber ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/wp-cli/scaffold-package-command/issues" 18 | }, 19 | "homepage": "https://github.com/wp-cli/scaffold-package-command#readme", 20 | "devDependencies": { 21 | "gherkin-lint": "^4.2.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | Custom ruleset for WP-CLI scaffold-package-command 4 | 5 | 12 | 13 | 14 | . 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | - 32 | 33 | 38 | 39 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /scaffold-package-command.php: -------------------------------------------------------------------------------- 1 | `, the command package is placed in the 21 | * WP-CLI `packages/local/` directory. 22 | * 23 | * ## OPTIONS 24 | * 25 | * 26 | * : Name for the new package. Expects / (e.g. 'wp-cli/scaffold-package'). 27 | * 28 | * [--description=] 29 | * : Human-readable description for the package. 30 | * 31 | * [--homepage=] 32 | * : Homepage for the package. Defaults to 'https://github.com/' 33 | * 34 | * [--dir=] 35 | * : Specify a destination directory for the command. Defaults to WP-CLI's `packages/local/` directory. 36 | * 37 | * [--license=] 38 | * : License for the package. 39 | * --- 40 | * default: MIT 41 | * --- 42 | * 43 | * [--require_wp_cli=] 44 | * : Required WP-CLI version for the package. 45 | * --- 46 | * default: ^2.11 47 | * --- 48 | * 49 | * [--require_wp_cli_tests=] 50 | * : Required WP-CLI testing framework version for the package. 51 | * --- 52 | * default: ^4.3.9 53 | * --- 54 | 55 | * [--skip-tests] 56 | * : Don't generate files for integration testing. 57 | * 58 | * [--skip-readme] 59 | * : Don't generate a README.md for the package. 60 | * 61 | * [--skip-github] 62 | * : Don't generate GitHub issue and pull request templates. 63 | * 64 | * [--skip-install] 65 | * : Don't install the package after scaffolding. 66 | * 67 | * [--force] 68 | * : Overwrite files that already exist. 69 | * 70 | * @when before_wp_load 71 | */ 72 | public function package( $args, $assoc_args ) { 73 | 74 | $defaults = [ 75 | 'dir' => '', 76 | 'description' => '', 77 | 'homepage' => '', 78 | ]; 79 | $assoc_args = array_merge( $defaults, $assoc_args ); 80 | $assoc_args['name'] = $args[0]; 81 | 82 | $bits = explode( '/', $assoc_args['name'] ); 83 | if ( 2 !== count( $bits ) || empty( $bits[0] ) || empty( $bits[1] ) ) { 84 | WP_CLI::error( "'{$assoc_args['name']}' is an invalid package name. Package scaffold expects '/'." ); 85 | } 86 | 87 | if ( ! empty( $assoc_args['dir'] ) ) { 88 | $package_dir = $assoc_args['dir']; 89 | } else { 90 | $package_dir = WP_CLI::get_runner()->get_packages_dir_path() . 'local/' . $assoc_args['name']; 91 | } 92 | 93 | if ( '~/' === substr( $package_dir, 0, 2 ) ) { 94 | $home = getenv( 'HOME' ); 95 | if ( $home ) { 96 | $package_dir = $home . substr( $package_dir, 1 ); 97 | } 98 | } 99 | 100 | if ( empty( $assoc_args['homepage'] ) ) { 101 | $assoc_args['homepage'] = 'https://github.com/' . $assoc_args['name']; 102 | } 103 | 104 | $force = Utils\get_flag_value( $assoc_args, 'force' ); 105 | 106 | $package_root = dirname( __DIR__ ); 107 | $template_path = $package_root . '/templates/'; 108 | $wp_cli_yml = <<create_files( 114 | [ 115 | "{$package_dir}/.gitignore" => file_get_contents( "{$package_root}/.gitignore" ), 116 | "{$package_dir}/.editorconfig" => file_get_contents( "{$package_root}/.editorconfig" ), 117 | "{$package_dir}/.distignore" => file_get_contents( "{$package_root}/.distignore" ), 118 | "{$package_dir}/CONTRIBUTING.md" => file_get_contents( "{$package_root}/CONTRIBUTING.md" ), 119 | "{$package_dir}/wp-cli.yml" => $wp_cli_yml, 120 | "{$package_dir}/hello-world-command.php" => Utils\mustache_render( "{$template_path}/hello-world-command.mustache", $assoc_args ), 121 | "{$package_dir}/src/HelloWorldCommand.php" => Utils\mustache_render( "{$template_path}/HelloWorldCommand.mustache", $assoc_args ), 122 | "{$package_dir}/composer.json" => Utils\mustache_render( "{$template_path}/composer.mustache", $assoc_args ), 123 | ], 124 | $force 125 | ); 126 | 127 | if ( empty( $files_written ) ) { 128 | WP_CLI::log( 'All package files were skipped.' ); 129 | } else { 130 | WP_CLI::success( "Created package files in {$package_dir}" ); 131 | } 132 | 133 | $force_flag = $force ? '--force' : ''; 134 | if ( ! Utils\get_flag_value( $assoc_args, 'skip-tests' ) ) { 135 | WP_CLI::runcommand( "scaffold package-tests {$package_dir} {$force_flag}", array( 'launch' => false ) ); 136 | } 137 | 138 | if ( ! Utils\get_flag_value( $assoc_args, 'skip-readme' ) ) { 139 | WP_CLI::runcommand( "scaffold package-readme {$package_dir} {$force_flag}", array( 'launch' => false ) ); 140 | } 141 | 142 | if ( ! Utils\get_flag_value( $assoc_args, 'skip-github' ) ) { 143 | WP_CLI::runcommand( "scaffold package-github {$package_dir} {$force_flag}", array( 'launch' => false ) ); 144 | } 145 | 146 | if ( ! Utils\get_flag_value( $assoc_args, 'skip-install' ) ) { 147 | WP_CLI::runcommand( "package install {$package_dir}", array( 'launch' => false ) ); 148 | } 149 | } 150 | 151 | /** 152 | * Generate a README.md for your command. 153 | * 154 | * Creates a README.md with Using, Installing, and Contributing instructions 155 | * based on the composer.json file for your WP-CLI package. Run this command 156 | * at the beginning of your project, and then every time your usage docs 157 | * change. 158 | * 159 | * These command-specific docs are generated based composer.json -> 'extra' 160 | * -> 'commands'. For instance, this package's composer.json includes: 161 | * 162 | * ``` 163 | * { 164 | * "name": "wp-cli/scaffold-package-command", 165 | * // [...] 166 | * "extra": { 167 | * "commands": [ 168 | * "scaffold package", 169 | * "scaffold package-tests", 170 | * "scaffold package-readme" 171 | * ] 172 | * } 173 | * } 174 | * ``` 175 | * 176 | * You can also customize the rendering of README.md generally with 177 | * composer.json -> 'extra' -> 'readme'. For example, runcommand/hook's 178 | * composer.json includes: 179 | * 180 | * ``` 181 | * { 182 | * "extra": { 183 | * "commands": [ 184 | * "hook" 185 | * ], 186 | * "readme": { 187 | * "shields": [ 188 | * "[![Build Status](https://travis-ci.org/runcommand/reset-password.svg?branch=master)](https://travis-ci.org/runcommand/reset-password)" 189 | * ], 190 | * "sections": [ 191 | * "Using", 192 | * "Installing", 193 | * "Support" 194 | * ], 195 | * "support": { 196 | * "body": "https://raw.githubusercontent.com/runcommand/runcommand-theme/master/bin/readme-partials/support-open-source.md" 197 | * }, 198 | * "show_powered_by": false 199 | * } 200 | * } 201 | * } 202 | * ``` 203 | * 204 | * In this example: 205 | * 206 | * * "shields" supports arbitrary images as shields to display. 207 | * * "sections" permits defining arbitrary sections (instead of default Using, Installing and Contributing). 208 | * * "support" -> "body" uses a remote Markdown file as the section contents. This can also be a local file path, or a string. 209 | * * "show_powered_by" shows or hides the Powered By mention at the end of the readme. 210 | * 211 | * For sections, "pre", "body" and "post" are supported. Example: 212 | * ``` 213 | * "support": { 214 | * "pre": "highlight.md", 215 | * "body": "https://raw.githubusercontent.com/runcommand/runcommand-theme/master/bin/readme-partials/support-open-source.md", 216 | * "post": "This is additional text to show after main body content." 217 | * }, 218 | * ``` 219 | * In this example: 220 | * 221 | * * "pre" content is pulled from local highlight.md file. 222 | * * "body" content is pulled from remote URL. 223 | * * "post" is a string. 224 | * 225 | * ## OPTIONS 226 | * 227 | * 228 | * : Directory path to an existing package to generate a readme for. 229 | * 230 | * [--force] 231 | * : Overwrite the readme if it already exists. 232 | * 233 | * [--branch=] 234 | * : Name of default branch of the underlying repository. Defaults to master. 235 | * 236 | * @when before_wp_load 237 | * @subcommand package-readme 238 | */ 239 | public function package_readme( $args, $assoc_args ) { 240 | 241 | list( $package_dir ) = $args; 242 | 243 | self::check_if_valid_package_dir( $package_dir ); 244 | 245 | $composer_obj = json_decode( file_get_contents( $package_dir . '/composer.json' ), true ); 246 | if ( ! $composer_obj ) { 247 | WP_CLI::error( 'Invalid composer.json in package directory.' ); 248 | } 249 | 250 | $force = Utils\get_flag_value( $assoc_args, 'force' ); 251 | $branch = Utils\get_flag_value( $assoc_args, 'branch', 'master' ); 252 | 253 | $package_root = dirname( __DIR__ ); 254 | $template_path = $package_root . '/templates/'; 255 | 256 | $bits = explode( '/', $composer_obj['name'] ); 257 | $readme_args = [ 258 | 'package_name' => $composer_obj['name'], 259 | 'package_short_name' => $bits[1], 260 | 'package_name_border' => str_pad( '', strlen( $composer_obj['name'] ), '=' ), 261 | 'package_description' => isset( $composer_obj['description'] ) ? $composer_obj['description'] : '', 262 | 'branch' => $branch, 263 | 'required_wp_cli_version' => ! empty( $composer_obj['require']['wp-cli/wp-cli'] ) ? str_replace( [ '~', '^', '>=' ], 'v', $composer_obj['require']['wp-cli/wp-cli'] ) : 'v1.3.0', 264 | 'shields' => '', 265 | 'has_commands' => false, 266 | 'wp_cli_update_to_instructions' => 'the latest stable release with `wp cli update`', 267 | 'show_powered_by' => isset( $composer_obj['extra']['readme']['show_powered_by'] ) ? (bool) $composer_obj['extra']['readme']['show_powered_by'] : true, 268 | ]; 269 | 270 | if ( isset( $composer_obj['extra']['readme']['shields'] ) ) { 271 | $readme_args['shields'] = implode( ' ', $composer_obj['extra']['readme']['shields'] ); 272 | } else { 273 | $shields = []; 274 | if ( file_exists( $package_dir . '/.github/workflows/testing.yml' ) ) { 275 | $shields[] = "[![Testing](https://github.com/{$readme_args['package_name']}/actions/workflows/testing.yml/badge.svg)](https://github.com/{$readme_args['package_name']}/actions/workflows/testing.yml)"; 276 | } 277 | if ( file_exists( $package_dir . '/.travis.yml' ) ) { 278 | $shields[] = "[![Build Status](https://travis-ci.org/{$readme_args['package_name']}.svg?branch={$branch})](https://travis-ci.org/{$readme_args['package_name']})"; 279 | } 280 | if ( file_exists( $package_dir . '/.circleci/config.yml' ) ) { 281 | $shields[] = "[![CircleCI](https://circleci.com/gh/{$readme_args['package_name']}/tree/{$branch}.svg?style=svg)](https://circleci.com/gh/{$readme_args['package_name']}/tree/{$branch})"; 282 | } 283 | if ( file_exists( $package_dir . '/codecov.yml' ) ) { 284 | $shields[] = "[![Code Coverage](https://codecov.io/gh/{$readme_args['package_name']}/branch/{$branch}/graph/badge.svg)](https://codecov.io/gh/{$readme_args['package_name']}/tree/{$branch})"; 285 | } 286 | 287 | if ( count( $shields ) ) { 288 | $readme_args['shields'] = implode( ' ', $shields ); 289 | } 290 | } 291 | 292 | $readme_args['wp_cli_requires_instructions'] = "requires WP-CLI {$readme_args['required_wp_cli_version']} or greater"; 293 | if ( '*' === $readme_args['required_wp_cli_version'] ) { 294 | $readme_args['wp_cli_requires_instructions'] = "requires WP-CLI's latest stable release"; 295 | } 296 | 297 | if ( false !== stripos( $readme_args['required_wp_cli_version'], 'alpha' ) ) { 298 | $readme_args['wp_cli_update_to_instructions'] = 'the latest nightly release with `wp cli update --nightly`'; 299 | } 300 | 301 | if ( ! empty( $composer_obj['extra']['commands'] ) ) { 302 | $readme_args['commands'] = []; 303 | $cmd_dump = WP_CLI::runcommand( 304 | 'cli cmd-dump', 305 | [ 306 | 'launch' => false, 307 | 'return' => true, 308 | 'parse' => 'json', 309 | ] 310 | ); 311 | foreach ( $composer_obj['extra']['commands'] as $command ) { 312 | $bits = explode( ' ', $command ); 313 | $parent_command = $cmd_dump; 314 | do { 315 | $cmd_bit = array_shift( $bits ); 316 | $found = false; 317 | foreach ( $parent_command['subcommands'] as $subcommand ) { 318 | if ( $subcommand['name'] === $cmd_bit ) { 319 | $parent_command = $subcommand; 320 | $found = true; 321 | break; 322 | } 323 | } 324 | if ( ! $found ) { 325 | $parent_command = false; 326 | } 327 | } while ( $parent_command && $bits ); 328 | 329 | /* This check doesn't work because of the way the commands are fetched. 330 | * Needs bigger refactor to put this check back in. 331 | if ( empty( $parent_command ) ) { 332 | WP_CLI::error( 'Missing one or more commands defined in composer.json -> extra -> commands.' ); 333 | } 334 | */ 335 | 336 | $longdesc = isset( $parent_command['longdesc'] ) ? $parent_command['longdesc'] : ''; 337 | $longdesc = preg_replace( '/## GLOBAL PARAMETERS(.+)/s', '', $longdesc ); 338 | $longdesc = preg_replace( '/##\s(.+)/', '**$1**', $longdesc ); 339 | 340 | // definition lists 341 | $longdesc = preg_replace_callback( '/([^\n]+)\n: (.+?)(\n\n|$)/s', [ __CLASS__, 'rewrap_param_desc' ], $longdesc ); 342 | 343 | $readme_args['commands'][] = [ 344 | 'name' => "wp {$command}", 345 | 'shortdesc' => isset( $parent_command['description'] ) ? $parent_command['description'] : '', 346 | 'synopsis' => "wp {$command}" . ( empty( $parent_command['subcommands'] ) ? ( isset( $parent_command['synopsis'] ) ? " {$parent_command['synopsis']}" : '' ) : '' ), 347 | 'longdesc' => $longdesc, 348 | ]; 349 | } 350 | $readme_args['has_commands'] = true; 351 | $readme_args['has_multiple_commands'] = count( $readme_args['commands'] ) > 1; 352 | } 353 | 354 | if ( isset( $composer_obj['extra']['readme']['sections'] ) ) { 355 | $readme_section_headings = $composer_obj['extra']['readme']['sections']; 356 | } else { 357 | $readme_section_headings = [ 358 | 'Using', 359 | 'Installing', 360 | 'Contributing', 361 | 'Support', 362 | ]; 363 | } 364 | 365 | $readme_sections = []; 366 | foreach ( $readme_section_headings as $section_heading ) { 367 | $key = strtolower( preg_replace( '#[^\da-z-_]#i', '', $section_heading ) ); 368 | $readme_sections[ $key ] = [ 369 | 'heading' => $section_heading, 370 | ]; 371 | } 372 | $bundled = ! empty( $composer_obj['extra']['bundled'] ); 373 | foreach ( [ 'using', 'installing', 'contributing', 'support' ] as $key ) { 374 | if ( isset( $readme_sections[ $key ] ) ) { 375 | $file = dirname( __DIR__ ) . '/templates/readme-' . $key . '.mustache'; 376 | if ( $bundled 377 | && file_exists( dirname( __DIR__ ) . '/templates/readme-' . $key . '-bundled.mustache' ) ) { 378 | $file = dirname( __DIR__ ) . '/templates/readme-' . $key . '-bundled.mustache'; 379 | } 380 | $readme_sections[ $key ]['body'] = $file; 381 | } 382 | } 383 | 384 | $readme_sections['package_description'] = [ 385 | 'body' => isset( $composer_obj['description'] ) ? $composer_obj['description'] : '', 386 | ]; 387 | 388 | $readme_args['quick_links'] = ''; 389 | foreach ( $readme_sections as $key => $section ) { 390 | if ( ! empty( $section['heading'] ) ) { 391 | $readme_args['quick_links'] .= '[' . $section['heading'] . '](#' . $key . ') | '; 392 | } 393 | } 394 | if ( ! empty( $readme_args['quick_links'] ) ) { 395 | $readme_args['quick_links'] = 'Quick links: ' . rtrim( $readme_args['quick_links'], '| ' ); 396 | } 397 | 398 | $readme_args['sections'] = []; 399 | $ext_regex = '#\.(md|mustache)$#i'; 400 | foreach ( $readme_sections as $section => $section_args ) { 401 | $value = []; 402 | foreach ( [ 'pre', 'body', 'post' ] as $k ) { 403 | $v = ''; 404 | if ( isset( $composer_obj['extra']['readme'][ $section ][ $k ] ) ) { 405 | $v = $composer_obj['extra']['readme'][ $section ][ $k ]; 406 | if ( filter_var( $v, FILTER_VALIDATE_URL ) === $v ) { 407 | $response = Utils\http_request( 'GET', $v ); 408 | $v = $response->body; 409 | } elseif ( preg_match( $ext_regex, $v ) ) { 410 | $v = $package_dir . '/' . $v; 411 | } 412 | } elseif ( isset( $section_args[ $k ] ) ) { 413 | $v = $section_args[ $k ]; 414 | } 415 | if ( $v ) { 416 | if ( preg_match( $ext_regex, $v ) ) { 417 | $v = Utils\mustache_render( $v, $readme_args ); 418 | } 419 | $value[] = trim( $v ); 420 | } 421 | } 422 | $value = trim( implode( PHP_EOL . PHP_EOL, $value ) ); 423 | if ( 'package_description' === $section ) { 424 | $readme_args['package_description'] = $value; 425 | } else { 426 | $readme_args['sections'][] = [ 427 | 'heading' => $section_args['heading'], 428 | 'body' => $value, 429 | ]; 430 | } 431 | } 432 | 433 | $files_written = $this->create_files( 434 | [ 435 | "{$package_dir}/README.md" => Utils\mustache_render( "{$template_path}/readme.mustache", $readme_args ), 436 | ], 437 | $force 438 | ); 439 | 440 | if ( empty( $files_written ) ) { 441 | WP_CLI::log( 'Package readme generation skipped.' ); 442 | } else { 443 | WP_CLI::success( 'Created package readme.' ); 444 | } 445 | } 446 | 447 | /** 448 | * Generate GitHub configuration files for your command. 449 | * 450 | * Creates a variety of files to better manage your project on GitHub. These 451 | * files include: 452 | * 453 | * * `.github/ISSUE_TEMPLATE` - Text displayed when a user opens a new issue. 454 | * * `.github/PULL_REQUEST_TEMPLATE` - Text displayed when a user submits a pull request. 455 | * * `.github/settings.yml` - Configuration file for the [Probot settings app](https://probot.github.io/apps/settings/). 456 | * 457 | * ## OPTIONS 458 | * 459 | * 460 | * : Directory path to an existing package to generate GitHub configuration for. 461 | * 462 | * [--force] 463 | * : Overwrite files that already exist. 464 | * 465 | * @when before_wp_load 466 | * @subcommand package-github 467 | */ 468 | public function package_github( $args, $assoc_args ) { 469 | list( $package_dir ) = $args; 470 | 471 | if ( is_file( $package_dir ) ) { 472 | $package_dir = dirname( $package_dir ); 473 | } elseif ( is_dir( $package_dir ) ) { 474 | $package_dir = rtrim( $package_dir, '/' ); 475 | } 476 | 477 | self::check_if_valid_package_dir( $package_dir ); 478 | 479 | $force = Utils\get_flag_value( $assoc_args, 'force' ); 480 | $template_path = dirname( __DIR__ ) . '/templates'; 481 | 482 | $composer_obj = json_decode( file_get_contents( $package_dir . '/composer.json' ), true ); 483 | $settings_vars = [ 484 | 'has_description' => false, 485 | 'description' => '', 486 | 'has_labels' => true, 487 | 'labels' => [ 488 | [ 489 | 'name' => 'bug', 490 | 'color' => 'fc2929', 491 | ], 492 | [ 493 | 'name' => 'scope:documentation', 494 | 'color' => '0e8a16', 495 | ], 496 | [ 497 | 'name' => 'scope:testing', 498 | 'color' => '5319e7', 499 | ], 500 | [ 501 | 'name' => 'good-first-issue', 502 | 'color' => 'eb6420', 503 | ], 504 | [ 505 | 'name' => 'help-wanted', 506 | 'color' => '159818', 507 | ], 508 | [ 509 | 'name' => 'maybelater', 510 | 'color' => 'c2e0c6', 511 | ], 512 | [ 513 | 'name' => 'state:unconfirmed', 514 | 'color' => 'bfe5bf', 515 | ], 516 | [ 517 | 'name' => 'state:unsupported', 518 | 'color' => 'bfe5bf', 519 | ], 520 | [ 521 | 'name' => 'wontfix', 522 | 'color' => 'c2e0c6', 523 | ], 524 | ], 525 | ]; 526 | if ( ! empty( $composer_obj['description'] ) ) { 527 | $settings_vars['description'] = $composer_obj['description']; 528 | $settings_vars['has_description'] = true; 529 | } 530 | if ( ! empty( $composer_obj['extra']['commands'] ) ) { 531 | foreach ( $composer_obj['extra']['commands'] as $cmd ) { 532 | $settings_vars['labels'][] = [ 533 | 'name' => 'command:' . str_replace( ' ', '-', $cmd ), 534 | 'color' => 'c5def5', 535 | ]; 536 | } 537 | } 538 | $create_files = [ 539 | "{$package_dir}/.github/ISSUE_TEMPLATE" => Utils\mustache_render( "{$template_path}/github-issue-template.mustache" ), 540 | "{$package_dir}/.github/PULL_REQUEST_TEMPLATE" => Utils\mustache_render( "{$template_path}/github-pull-request-template.mustache" ), 541 | "{$package_dir}/.github/settings.yml" => Utils\mustache_render( "{$template_path}/github-settings.mustache", $settings_vars ), 542 | ]; 543 | $files_written = $this->create_files( $create_files, $force ); 544 | if ( empty( $files_written ) ) { 545 | WP_CLI::log( 'Package GitHub configuration generation skipped.' ); 546 | } else { 547 | WP_CLI::success( 'Created package GitHub configuration.' ); 548 | } 549 | } 550 | 551 | /** 552 | * Generate files for writing Behat tests for your command. 553 | * 554 | * WP-CLI makes use of a Behat-based testing framework, which you should use 555 | * too. This command generates all of the files you need. Functional tests 556 | * are an integral ingredient of high-quality, maintainable commands. 557 | * Behat is a great choice as a testing framework because: 558 | * 559 | * * It’s easy to write new tests, which means they’ll actually get written. 560 | * * The tests interface with your command in the same manner as your users 561 | * interface with your command, and they describe how the command is 562 | * expected to work in human-readable terms. 563 | * 564 | * Behat tests live in the `features/` directory of your project. When you 565 | * use this command, it will generate a default test that looks like this: 566 | * 567 | * ``` 568 | * Feature: Test that WP-CLI loads. 569 | * 570 | * Scenario: WP-CLI loads for your tests 571 | * Given a WP install 572 | * 573 | * When I run `wp eval 'echo "Hello world.";'` 574 | * Then STDOUT should contain: 575 | * """ 576 | * Hello world. 577 | * """ 578 | * ``` 579 | * 580 | * Functional tests typically follow this pattern: 581 | * 582 | * * **Given** some background, 583 | * * **When** a user performs a specific action, 584 | * * **Then** the end result should be X (and Y and Z). 585 | * 586 | * View all defined Behat steps available for use with `behat -dl`: 587 | * 588 | * ``` 589 | * Given /^an empty directory$/ 590 | * Given /^an empty cache/ 591 | * Given /^an? ([^\s]+) file:$/ 592 | * Given /^"([^"]+)" replaced with "([^"]+)" in the ([^\s]+) file$/ 593 | * ``` 594 | * 595 | * The files generated by this command include: 596 | * 597 | * * `.travis.yml` is the configuration file for Travis CI. 598 | * * `bin/install-package-tests.sh` will configure your environment to run 599 | * the tests. 600 | * * `bin/test.sh` is a test runner that respects contextual Behat tags. 601 | * * `features/load-wp-cli.feature` is a basic test to confirm WP-CLI can 602 | * load. 603 | * * `features/bootstrap`, `features/steps`, `features/extra` are Behat 604 | * configuration files. 605 | * 606 | * After running `bin/install-package-tests.sh`, you can run the tests with 607 | * `./vendor/bin/behat`. If you find yourself using Behat on a number of 608 | * projects and don't want to install a copy with each one, you can 609 | * `composer global require behat/behat` to install Behat globally on your 610 | * machine. Make sure `~/.composer/vendor/bin` has also been added to your 611 | * `$PATH`. Once you've done so, you can run the tests for a project by 612 | * calling `behat`. 613 | * 614 | * For Travis CI, specially-named files in the package directory can be 615 | * used to modify the generated `.travis.yml`, where `` is one of 616 | * 'cache', 'env', 'matrix', 'before_install', 'install', 'before_script', 'script': 617 | * * `travis-.yml` - contents used for `:` (if present following ignored) 618 | * * `travis--append.yml` - contents appended to generated `:` 619 | * 620 | * You can also append to the generated `.travis.yml` with the file: 621 | * * `travis-append.yml` - contents appended to generated `.travis.yml` 622 | * 623 | * ## ENVIRONMENT 624 | * 625 | * The `features/bootstrap/FeatureContext.php` file expects the 626 | * WP_CLI_BIN_DIR environment variable. 627 | * 628 | * WP-CLI Behat framework uses Behat ~2.5, which is installed with Composer. 629 | * 630 | * ## OPTIONS 631 | * 632 | * 633 | * : Directory path to an existing package to generate tests for. 634 | * 635 | * [--ci=] 636 | * : Create a configuration file for a specific CI provider. 637 | * --- 638 | * default: travis 639 | * options: 640 | * - travis 641 | * - circle 642 | * - github 643 | * --- 644 | * 645 | * [--force] 646 | * : Overwrite files that already exist. 647 | * 648 | * ## EXAMPLES 649 | * 650 | * # Generate files for writing Behat tests. 651 | * $ wp scaffold package-tests /path/to/command/dir/ 652 | * Success: Created package test files. 653 | * 654 | * @when before_wp_load 655 | * @subcommand package-tests 656 | */ 657 | public function package_tests( $args, $assoc_args ) { 658 | list( $package_dir ) = $args; 659 | 660 | if ( is_file( $package_dir ) ) { 661 | $package_dir = dirname( $package_dir ); 662 | } elseif ( is_dir( $package_dir ) ) { 663 | $package_dir = rtrim( $package_dir, '/' ); 664 | } 665 | 666 | self::check_if_valid_package_dir( $package_dir ); 667 | 668 | $package_dir .= '/'; 669 | $features_dir = $package_dir . 'features/'; 670 | if ( ! is_dir( $features_dir ) ) { 671 | Process::create( Utils\esc_cmd( 'mkdir %s', $features_dir ) )->run(); 672 | } 673 | 674 | $package_root = dirname( __DIR__ ); 675 | 676 | // Only create a sample feature file when none exist 677 | if ( ! glob( $features_dir . '/*.feature' ) ) { 678 | $copy_source[ $package_root ]['templates/load-wp-cli.feature'] = $features_dir; 679 | } 680 | 681 | $copy_source[ $package_root ]['behat.yml'] = $package_dir; 682 | 683 | $travis_tags = [ 'cache', 'env', 'matrix', 'before_install', 'install', 'before_script', 'script' ]; 684 | $travis_tag_overwrites = []; 685 | $travis_tag_appends = []; 686 | $travis_append = ''; 687 | if ( 'travis' === $assoc_args['ci'] ) { 688 | $copy_source[ $package_root ]['templates/.travis.yml'] = $package_dir; 689 | 690 | // Allow a package to overwrite or append to Travis tags. 691 | foreach ( $travis_tags as $travis_tag ) { 692 | if ( file_exists( $package_dir . 'travis-' . $travis_tag . '.yml' ) ) { 693 | $travis_tag_overwrites[ $travis_tag ] = file_get_contents( $package_dir . 'travis-' . $travis_tag . '.yml' ); 694 | } elseif ( file_exists( $package_dir . 'travis-' . $travis_tag . '-append.yml' ) ) { 695 | $travis_tag_appends[ $travis_tag ] = file_get_contents( $package_dir . 'travis-' . $travis_tag . '-append.yml' ); 696 | } 697 | } 698 | 699 | // Allow a package to append to Travis. 700 | if ( file_exists( $package_dir . 'travis-append.yml' ) ) { 701 | $travis_append = file_get_contents( $package_dir . 'travis-append.yml' ); 702 | } 703 | } elseif ( 'circle' === $assoc_args['ci'] ) { 704 | $copy_source[ $package_root ]['.circleci/config.yml'] = $package_dir . '.circleci/'; 705 | } elseif ( 'github' === $assoc_args['ci'] ) { 706 | $copy_source[ $package_root ]['templates/testing.yml'] = $package_dir . '.github/workflows/'; 707 | } 708 | 709 | $files_written = []; 710 | foreach ( $copy_source as $source => $to_copy ) { 711 | foreach ( $to_copy as $file => $dir ) { 712 | if ( 'php/WP_CLI/ProcessRun.php' === $file && ! file_exists( $source . "/{$file}" ) ) { 713 | continue; 714 | } 715 | // file_get_contents() works with Phar-archived files 716 | $contents = file_get_contents( $source . "/{$file}" ); 717 | $file_path = $dir . basename( $file ); 718 | 719 | if ( '.travis.yml' === $file ) { 720 | foreach ( $travis_tags as $travis_tag ) { 721 | if ( isset( $travis_tag_overwrites[ $travis_tag ] ) ) { 722 | // Note the contents fully overwrite, so should include the tag, eg `env:` etc (or nothing if want to remove totally). 723 | $contents = preg_replace( '/^' . $travis_tag . ':.*?(?:^$|\z)/ms', $travis_tag_overwrites[ $travis_tag ], $contents ); 724 | } elseif ( isset( $travis_tag_appends[ $travis_tag ] ) ) { 725 | $contents = preg_replace( '/^' . $travis_tag . ':.*?(?:^$|\z)/ms', '\0' . $travis_tag_appends[ $travis_tag ], $contents ); 726 | } 727 | } 728 | if ( $travis_append ) { 729 | $contents .= $travis_append; 730 | } 731 | } 732 | 733 | $force = Utils\get_flag_value( $assoc_args, 'force' ); 734 | $should_write_file = $this->prompt_if_files_will_be_overwritten( $file_path, $force ); 735 | if ( ! $should_write_file ) { 736 | continue; 737 | } 738 | $files_written[] = $file_path; 739 | 740 | if ( ! is_dir( dirname( $file_path ) ) ) { 741 | Process::create( Utils\esc_cmd( 'mkdir -p %s', dirname( $file_path ) ) )->run(); 742 | } 743 | 744 | Process::create( Utils\esc_cmd( 'touch %s', $file_path ) )->run(); 745 | file_put_contents( $file_path, $contents ); 746 | } 747 | } 748 | 749 | if ( empty( $files_written ) ) { 750 | WP_CLI::log( 'All package test files were skipped.' ); 751 | } else { 752 | WP_CLI::success( 'Created package test files.' ); 753 | } 754 | } 755 | 756 | private static function rewrap_param_desc( $matches ) { 757 | $param = $matches[1]; 758 | $desc = self::indent( "\t\t", $matches[2] ); 759 | return "\t$param\n$desc\n\n"; 760 | } 761 | 762 | private static function indent( $whitespace, $text ) { 763 | $lines = explode( "\n", $text ); 764 | foreach ( $lines as &$line ) { 765 | $line = $whitespace . $line; 766 | } 767 | return implode( "\n", $lines ); 768 | } 769 | 770 | private function prompt_if_files_will_be_overwritten( $filename, $force ) { 771 | $should_write_file = true; 772 | if ( ! file_exists( $filename ) ) { 773 | return true; 774 | } 775 | 776 | WP_CLI::warning( 'File already exists' ); 777 | WP_CLI::log( $filename ); 778 | if ( ! $force ) { 779 | do { 780 | $answer = \cli\prompt( 781 | 'Skip this file, or replace it with scaffolding?', 782 | $default = false, 783 | $marker = '[s/r]: ' 784 | ); 785 | } while ( ! in_array( $answer, [ 's', 'r' ], true ) ); 786 | $should_write_file = 'r' === $answer; 787 | } 788 | 789 | $outcome = $should_write_file ? 'Replacing' : 'Skipping'; 790 | WP_CLI::log( $outcome . PHP_EOL ); 791 | 792 | return $should_write_file; 793 | } 794 | 795 | private function create_files( $files_and_contents, $force ) { 796 | $wrote_files = []; 797 | 798 | foreach ( $files_and_contents as $filename => $contents ) { 799 | $should_write_file = $this->prompt_if_files_will_be_overwritten( $filename, $force ); 800 | if ( ! $should_write_file ) { 801 | continue; 802 | } 803 | 804 | if ( ! is_dir( dirname( $filename ) ) ) { 805 | Process::create( Utils\esc_cmd( 'mkdir -p %s', dirname( $filename ) ) )->run(); 806 | } 807 | 808 | if ( ! file_put_contents( $filename, $contents ) ) { 809 | WP_CLI::error( "Error creating file: $filename" ); 810 | } elseif ( $should_write_file ) { 811 | $wrote_files[] = $filename; 812 | } 813 | } 814 | return $wrote_files; 815 | } 816 | 817 | private static function check_if_valid_package_dir( $package_dir ) { 818 | if ( ! is_dir( $package_dir ) ) { 819 | WP_CLI::error( 'Directory does not exist.' ); 820 | } 821 | 822 | if ( ! file_exists( $package_dir . '/composer.json' ) ) { 823 | WP_CLI::error( 'Invalid package directory. composer.json file must be present.' ); 824 | } 825 | } 826 | } 827 | -------------------------------------------------------------------------------- /templates/.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | 4 | language: php 5 | php: 7.4 6 | 7 | services: 8 | - mysql 9 | 10 | notifications: 11 | email: 12 | on_success: never 13 | on_failure: change 14 | 15 | branches: 16 | only: 17 | - master 18 | 19 | cache: 20 | directories: 21 | - $HOME/.composer/cache 22 | 23 | env: 24 | global: 25 | - PATH="$TRAVIS_BUILD_DIR/vendor/bin:$PATH" 26 | - WP_CLI_BIN_DIR="$TRAVIS_BUILD_DIR/vendor/bin" 27 | 28 | before_install: 29 | - | 30 | # Remove Xdebug for a huge performance increase: 31 | if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then 32 | phpenv config-rm xdebug.ini 33 | else 34 | echo "xdebug.ini does not exist" 35 | fi 36 | - | 37 | # Raise PHP memory limit to 2048MB 38 | echo 'memory_limit = 2048M' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini 39 | - composer validate 40 | 41 | install: 42 | - composer install 43 | - composer prepare-tests 44 | 45 | script: 46 | - composer phpunit 47 | - composer behat || composer behat-rerun 48 | 49 | jobs: 50 | include: 51 | - stage: test 52 | php: nightly 53 | env: WP_VERSION=trunk 54 | - stage: test 55 | php: 7.4 56 | env: WP_VERSION=latest 57 | - stage: test 58 | php: 7.3 59 | env: WP_VERSION=latest 60 | - stage: test 61 | php: 7.2 62 | env: WP_VERSION=latest 63 | - stage: test 64 | php: 7.1 65 | env: WP_VERSION=latest 66 | - stage: test 67 | php: 7.0 68 | env: WP_VERSION=latest 69 | - stage: test 70 | php: 5.6 71 | env: WP_VERSION=latest 72 | - stage: test 73 | php: 5.6 74 | env: WP_VERSION=3.7.11 75 | dist: trusty 76 | - stage: test 77 | php: 5.6 78 | env: WP_VERSION=trunk 79 | 80 | allow_failures: 81 | - stage: test 82 | php: nightly 83 | env: WP_VERSION=trunk 84 | -------------------------------------------------------------------------------- /templates/HelloWorldCommand.mustache: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /templates/github-pull-request-template.mustache: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /templates/github-settings.mustache: -------------------------------------------------------------------------------- 1 | # Used by Probot Settings: https://probot.github.io/apps/settings/ 2 | repository: 3 | {{#has_description}} 4 | description: {{description}} 5 | {{/has_description}} 6 | {{#has_labels}} 7 | labels: 8 | {{#labels}} 9 | - name: {{name}} 10 | color: {{color}} 11 | {{/labels}} 12 | {{/has_labels}} 13 | -------------------------------------------------------------------------------- /templates/hello-world-command.mustache: -------------------------------------------------------------------------------- 1 |