Skip to content

Commit 66fdb47

Browse files
authored
Full implementation of --license argument (#288)
1 parent 270154b commit 66fdb47

24 files changed

Lines changed: 3771 additions & 18 deletions

README.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,31 @@ WP-CLI `packages/local/` directory.
4545
Specify a destination directory for the command. Defaults to WP-CLI's `packages/local/` directory.
4646

4747
[--license=<license>]
48-
License for the package.
48+
License for the package by SPDX code.
4949
---
5050
default: MIT
51+
options:
52+
- MIT
53+
- Apache-2.0
54+
- BSD-3-Clause
55+
- BSD-2-Clause
56+
- GPL-2.0-only
57+
- GPL-3.0-only
58+
- ISC
59+
- LGPL-3.0-only
60+
- LGPL-2.1-only
61+
- 0BSD
62+
- AGPL-3.0-only
63+
- MPL-2.0
64+
- AFL-3.0
65+
- MS-PL
66+
- BSD-1-Clause
67+
- MIT-0
68+
- CPL-1.0
69+
- BSL-1.0
70+
- Unlicense
71+
- CC0-1.0
72+
- none
5173
---
5274

5375
[--require_wp_cli=<version>]
@@ -279,9 +301,30 @@ In this example:
279301
Name of default branch of the underlying repository. Defaults to main.
280302

281303
[--license=<license>]
282-
License for the package.
304+
License for the package by SPDX code.
283305
---
284-
default: MIT
306+
options:
307+
- MIT
308+
- Apache-2.0
309+
- BSD-3-Clause
310+
- BSD-2-Clause
311+
- GPL-2.0-only
312+
- GPL-3.0-only
313+
- ISC
314+
- LGPL-3.0-only
315+
- LGPL-2.1-only
316+
- 0BSD
317+
- AGPL-3.0-only
318+
- MPL-2.0
319+
- AFL-3.0
320+
- MS-PL
321+
- BSD-1-Clause
322+
- MIT-0
323+
- CPL-1.0
324+
- BSL-1.0
325+
- Unlicense
326+
- CC0-1.0
327+
- none
285328
---
286329

287330

features/scaffold-package-readme.feature

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,3 +523,55 @@ Feature: Scaffold a README.md file for an existing package
523523
Then the foo/README.md file should exist
524524
And the contents of the foo/README.md file should match /\t\tShow extended version information/
525525
And the contents of the foo/README.md file should match /\t\tNote: to retrieve/
526+
527+
Scenario: Scaffold a README.md and generate a non-MIT license
528+
Given an empty directory
529+
And a foo/composer.json file:
530+
"""
531+
{
532+
"name": "wp-cli/foo",
533+
"description": "A test package.",
534+
"license": "MIT",
535+
"require": {
536+
"wp-cli/wp-cli": "^2.13"
537+
}
538+
}
539+
"""
540+
541+
When I run `wp scaffold package-readme foo --license=GPL-2.0-only`
542+
Then the foo/README.md file should exist
543+
And the foo/LICENSE file should exist
544+
And the foo/LICENSE file should contain:
545+
"""
546+
GNU GENERAL PUBLIC LICENSE
547+
"""
548+
And the foo/composer.json file should contain:
549+
"""
550+
"license": "GPL-2.0-only"
551+
"""
552+
553+
Scenario: Scaffold a README.md with --license=none removes LICENSE and sets proprietary
554+
Given an empty directory
555+
And a foo/composer.json file:
556+
"""
557+
{
558+
"name": "wp-cli/foo",
559+
"description": "A test package.",
560+
"license": "MIT",
561+
"require": {
562+
"wp-cli/wp-cli": "^2.13"
563+
}
564+
}
565+
"""
566+
And a foo/LICENSE file:
567+
"""
568+
Some existing license content.
569+
"""
570+
571+
When I run `wp scaffold package-readme foo --license=none`
572+
Then the foo/README.md file should exist
573+
And the foo/LICENSE file should not exist
574+
And the foo/composer.json file should contain:
575+
"""
576+
"license": "proprietary"
577+
"""

features/scaffold-package.feature

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ Feature: Scaffold WP-CLI commands
5555
And the {PACKAGE_PATH}/local/wp-cli/foo/LICENSE file should exist
5656
And the {PACKAGE_PATH}/local/wp-cli/foo/LICENSE file should contain:
5757
"""
58-
The MIT License (MIT)
58+
MIT License
5959
"""
6060
And the {PACKAGE_PATH}/local/wp-cli/foo/LICENSE file should contain:
6161
"""
62-
wp-cli/foo Contributors
62+
wp-cli/foo
6363
"""
6464
And the {PACKAGE_PATH}/local/wp-cli/foo/wp-cli.yml file should exist
6565
And the {PACKAGE_PATH}/local/wp-cli/foo/.travis.yml file should not exist
@@ -320,3 +320,41 @@ Feature: Scaffold WP-CLI commands
320320
"""
321321
https://getcomposer.org/doc/01-basic-usage.md#autoloading
322322
"""
323+
324+
Scenario: Scaffold a WP-CLI package with Apache-2.0 license
325+
Given an empty directory
326+
327+
When I run `wp package path`
328+
Then save STDOUT as {PACKAGE_PATH}
329+
330+
When I run `wp scaffold package wp-cli/apache-pkg --skip-tests --skip-github --skip-install --skip-readme --license=Apache-2.0`
331+
Then STDOUT should contain:
332+
"""
333+
Success: Created package files
334+
"""
335+
And the {PACKAGE_PATH}/local/wp-cli/apache-pkg/LICENSE file should exist
336+
And the {PACKAGE_PATH}/local/wp-cli/apache-pkg/LICENSE file should contain:
337+
"""
338+
Apache License
339+
"""
340+
And the {PACKAGE_PATH}/local/wp-cli/apache-pkg/composer.json file should contain:
341+
"""
342+
"license": "Apache-2.0",
343+
"""
344+
345+
Scenario: Scaffold a WP-CLI package with no license
346+
Given an empty directory
347+
348+
When I run `wp package path`
349+
Then save STDOUT as {PACKAGE_PATH}
350+
351+
When I run `wp scaffold package wp-cli/no-license --skip-tests --skip-github --skip-install --skip-readme --license=none`
352+
Then STDOUT should contain:
353+
"""
354+
Success: Created package files
355+
"""
356+
And the {PACKAGE_PATH}/local/wp-cli/no-license/LICENSE file should not exist
357+
And the {PACKAGE_PATH}/local/wp-cli/no-license/composer.json file should contain:
358+
"""
359+
"license": "proprietary",
360+
"""

src/ScaffoldPackageCommand.php

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use WP_CLI\Path;
88

99
/**
10-
* @phpstan-type ComposerConfig array{name: string, description: string, extra: array{readme: array{shields: array<string>}, commands: array<string>}, require: array<string, string>, 'require-dev': array<string, string>}
10+
* @phpstan-type ComposerConfig array{name: string, description: string, license?: string, extra: array{readme: array{shields: array<string>}, commands: array<string>}, require: array<string, string>, 'require-dev': array<string, string>}
1111
*/
1212
class ScaffoldPackageCommand {
1313

@@ -40,9 +40,31 @@ class ScaffoldPackageCommand {
4040
* : Specify a destination directory for the command. Defaults to WP-CLI's `packages/local/` directory.
4141
*
4242
* [--license=<license>]
43-
* : License for the package.
43+
* : License for the package by SPDX code.
4444
* ---
4545
* default: MIT
46+
* options:
47+
* - MIT
48+
* - Apache-2.0
49+
* - BSD-3-Clause
50+
* - BSD-2-Clause
51+
* - GPL-2.0-only
52+
* - GPL-3.0-only
53+
* - ISC
54+
* - LGPL-3.0-only
55+
* - LGPL-2.1-only
56+
* - 0BSD
57+
* - AGPL-3.0-only
58+
* - MPL-2.0
59+
* - AFL-3.0
60+
* - MS-PL
61+
* - BSD-1-Clause
62+
* - MIT-0
63+
* - CPL-1.0
64+
* - BSL-1.0
65+
* - Unlicense
66+
* - CC0-1.0
67+
* - none
4668
* ---
4769
*
4870
* [--require_wp_cli=<version>]
@@ -84,6 +106,13 @@ public function package( $args, $assoc_args ) {
84106
$assoc_args['name'] = $args[0];
85107
$assoc_args['year'] = gmdate( 'Y' );
86108

109+
$license_input = Utils\get_flag_value( $assoc_args, 'license', 'MIT' );
110+
if ( 'none' === strtolower( $license_input ) ) {
111+
$assoc_args['license'] = 'proprietary';
112+
} else {
113+
$assoc_args['license'] = $license_input;
114+
}
115+
87116
$bits = explode( '/', $assoc_args['name'] );
88117
if ( 2 !== count( $bits ) || empty( $bits[0] ) || empty( $bits[1] ) ) {
89118
WP_CLI::error( "'{$assoc_args['name']}' is an invalid package name. Package scaffold expects '<author>/<package>'." );
@@ -134,7 +163,7 @@ public function package( $args, $assoc_args ) {
134163
"{$package_dir}/.distignore" => file_get_contents( "{$package_root}/.distignore" ),
135164
"{$package_dir}/phpcs.xml.dist" => Utils\mustache_render( "{$template_path}/phpcs.xml.dist.mustache", $assoc_args ),
136165
"{$package_dir}/CONTRIBUTING.md" => file_get_contents( "{$package_root}/CONTRIBUTING.md" ),
137-
"{$package_dir}/LICENSE" => Utils\mustache_render( "{$template_path}/LICENSE.mustache", $assoc_args ),
166+
138167
"{$package_dir}/wp-cli.yml" => $wp_cli_yml,
139168
"{$package_dir}/hello-world-command.php" => Utils\mustache_render( "{$template_path}/hello-world-command.mustache", $assoc_args ),
140169
"{$package_dir}/src/HelloWorldCommand.php" => Utils\mustache_render( "{$template_path}/HelloWorldCommand.mustache", $assoc_args ),
@@ -166,7 +195,15 @@ public function package( $args, $assoc_args ) {
166195
}
167196

168197
if ( ! Utils\get_flag_value( $assoc_args, 'skip-readme' ) ) {
169-
WP_CLI::runcommand( "scaffold package-readme {$quoted_package_dir} {$force_flag}", array( 'launch' => false ) );
198+
WP_CLI::runcommand( "scaffold package-readme {$quoted_package_dir} {$force_flag} --license={$license_input}", array( 'launch' => false ) );
199+
} elseif ( 'none' !== strtolower( $license_input ) ) {
200+
$license_file_path = $this->resolve_license_file_path( $license_input, $template_path );
201+
if ( null !== $license_file_path ) {
202+
$this->create_files(
203+
[ "{$package_dir}/LICENSE" => Utils\mustache_render( $license_file_path, $assoc_args ) ],
204+
$force
205+
);
206+
}
170207
}
171208

172209
// Display next steps guidance for users.
@@ -263,9 +300,30 @@ public function package( $args, $assoc_args ) {
263300
* : Name of default branch of the underlying repository. Defaults to main.
264301
*
265302
* [--license=<license>]
266-
* : License for the package.
303+
* : License for the package by SPDX code.
267304
* ---
268-
* default: MIT
305+
* options:
306+
* - MIT
307+
* - Apache-2.0
308+
* - BSD-3-Clause
309+
* - BSD-2-Clause
310+
* - GPL-2.0-only
311+
* - GPL-3.0-only
312+
* - ISC
313+
* - LGPL-3.0-only
314+
* - LGPL-2.1-only
315+
* - 0BSD
316+
* - AGPL-3.0-only
317+
* - MPL-2.0
318+
* - AFL-3.0
319+
* - MS-PL
320+
* - BSD-1-Clause
321+
* - MIT-0
322+
* - CPL-1.0
323+
* - BSL-1.0
324+
* - Unlicense
325+
* - CC0-1.0
326+
* - none
269327
* ---
270328
*
271329
* @when before_wp_load
@@ -291,6 +349,12 @@ public function package_readme( $args, $assoc_args ) {
291349
$package_root = dirname( __DIR__ );
292350
$template_path = $package_root . '/templates/';
293351

352+
$license_input = Utils\get_flag_value( $assoc_args, 'license' );
353+
if ( null === $license_input ) {
354+
$license_input = isset( $composer_obj['license'] ) ? $composer_obj['license'] : 'MIT';
355+
}
356+
$composer_license = 'none' === strtolower( $license_input ) ? 'proprietary' : $license_input;
357+
294358
$bits = explode( '/', $composer_obj['name'] );
295359
$readme_args = [
296360
'package_name' => $composer_obj['name'],
@@ -303,7 +367,7 @@ public function package_readme( $args, $assoc_args ) {
303367
'has_commands' => false,
304368
'wp_cli_update_to_instructions' => 'the latest stable release with `wp cli update`',
305369
'show_powered_by' => isset( $composer_obj['extra']['readme']['show_powered_by'] ) ? (bool) $composer_obj['extra']['readme']['show_powered_by'] : true,
306-
'license' => $assoc_args['license'],
370+
'license' => $composer_license,
307371
];
308372

309373
if ( isset( $composer_obj['extra']['readme']['shields'] ) ) {
@@ -538,6 +602,35 @@ public function package_readme( $args, $assoc_args ) {
538602
} else {
539603
WP_CLI::success( 'Created package readme.' );
540604
}
605+
606+
// Only write to composer.json if the license has changed. This protects package_readme from
607+
// overwriting files changed by package. Only when --license changes do we need to write.
608+
$existing_license = isset( $composer_obj['license'] ) ? $composer_obj['license'] : '';
609+
$composer_obj['license'] = $composer_license;
610+
if ( $composer_license !== $existing_license ) {
611+
$composer_json_content = json_encode( $composer_obj, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) . "\n";
612+
if ( false === file_put_contents( $package_dir . '/composer.json', $composer_json_content ) ) {
613+
WP_CLI::error( "Error creating file: {$package_dir}/composer.json" );
614+
}
615+
}
616+
617+
if ( 'none' === strtolower( $license_input ) ) {
618+
if ( is_file( $package_dir . '/LICENSE' ) ) {
619+
unlink( $package_dir . '/LICENSE' );
620+
}
621+
} else {
622+
$license_file_path = $this->resolve_license_file_path( $license_input, $template_path );
623+
if ( null !== $license_file_path ) {
624+
$license_data = [
625+
'year' => gmdate( 'Y' ),
626+
'name' => $composer_obj['name'],
627+
];
628+
$this->create_files(
629+
[ "{$package_dir}/LICENSE" => Utils\mustache_render( $license_file_path, $license_data ) ],
630+
$force
631+
);
632+
}
633+
}
541634
}
542635

543636
/**
@@ -835,6 +928,18 @@ public function package_tests( $args, $assoc_args ) {
835928
}
836929
}
837930

931+
private function resolve_license_file_path( $license, $template_path ) {
932+
$normalized = strtolower( $license );
933+
if ( basename( $normalized ) !== $normalized ) {
934+
return null;
935+
}
936+
$license_path = $template_path . 'licenses/' . $normalized;
937+
if ( is_file( $license_path ) ) {
938+
return $license_path;
939+
}
940+
return null;
941+
}
942+
838943
private static function rewrap_param_desc( $matches ) {
839944
$param = $matches[1];
840945
$desc = self::indent( "\t\t", self::normalize_desc_indentation( rtrim( $matches[2] ) ) );

templates/licenses/0bsd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
BSD Zero Clause License
2+
3+
Copyright (c) {{year}} {{name}}
4+
5+
Permission to use, copy, modify, and/or distribute this software for any
6+
purpose with or without fee is hereby granted.
7+
8+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
PERFORMANCE OF THIS SOFTWARE.

0 commit comments

Comments
 (0)