@wpelevator/wp-release 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +150 -19
  2. package/dist/changeset-config.d.ts +91 -5
  3. package/dist/changeset-config.js +108 -11
  4. package/dist/changeset-dependents.d.ts +19 -8
  5. package/dist/changeset-dependents.js +96 -34
  6. package/dist/changeset-files.d.ts +6 -5
  7. package/dist/changeset-files.js +7 -5
  8. package/dist/changeset-packages.d.ts +55 -8
  9. package/dist/changeset-packages.js +85 -14
  10. package/dist/changeset-prompt.d.ts +119 -0
  11. package/dist/changeset-prompt.js +203 -0
  12. package/dist/changeset-release.d.ts +43 -5
  13. package/dist/changeset-release.js +99 -16
  14. package/dist/changesets.d.ts +6 -2
  15. package/dist/changesets.js +18 -12
  16. package/dist/cli.d.ts +7 -1
  17. package/dist/cli.js +51 -14
  18. package/dist/commands/args.d.ts +0 -21
  19. package/dist/commands/args.js +1 -35
  20. package/dist/commands/changeset-add.d.ts +5 -2
  21. package/dist/commands/changeset-add.js +109 -9
  22. package/dist/commands/changeset-args.d.ts +24 -5
  23. package/dist/commands/changeset-args.js +57 -14
  24. package/dist/commands/changeset-status.d.ts +3 -1
  25. package/dist/commands/changeset-status.js +2 -1
  26. package/dist/commands/changeset-tag.d.ts +1 -1
  27. package/dist/commands/changeset-tag.js +6 -3
  28. package/dist/commands/changeset-version.d.ts +7 -1
  29. package/dist/commands/changeset-version.js +17 -4
  30. package/dist/commands/checksums.js +2 -2
  31. package/dist/commands/command.d.ts +5 -0
  32. package/dist/commands/deploy-git.d.ts +24 -4
  33. package/dist/commands/deploy-git.js +57 -3
  34. package/dist/commands/deploy-svn.d.ts +24 -4
  35. package/dist/commands/deploy-svn.js +57 -2
  36. package/dist/commands/keygen.d.ts +3 -0
  37. package/dist/commands/keygen.js +6 -1
  38. package/dist/commands/package-deploys.d.ts +86 -0
  39. package/dist/commands/package-deploys.js +113 -0
  40. package/dist/commands/sign.d.ts +19 -1
  41. package/dist/commands/sign.js +47 -12
  42. package/dist/commands/verify.d.ts +3 -0
  43. package/dist/commands/verify.js +43 -24
  44. package/dist/commands/zip.d.ts +12 -8
  45. package/dist/commands/zip.js +135 -78
  46. package/dist/ed25519.d.ts +25 -0
  47. package/dist/ed25519.js +44 -0
  48. package/dist/file-selection.d.ts +21 -0
  49. package/dist/file-selection.js +35 -0
  50. package/dist/git-deploy.d.ts +32 -0
  51. package/dist/git-deploy.js +57 -1
  52. package/dist/git.d.ts +17 -0
  53. package/dist/git.js +23 -0
  54. package/dist/index.d.ts +13 -5
  55. package/dist/index.js +7 -3
  56. package/dist/minisign.d.ts +41 -0
  57. package/dist/minisign.js +128 -0
  58. package/dist/package-deploy.d.ts +133 -0
  59. package/dist/package-deploy.js +323 -0
  60. package/dist/package-zip.d.ts +52 -0
  61. package/dist/package-zip.js +78 -0
  62. package/dist/release-repository.d.ts +18 -5
  63. package/dist/release-repository.js +51 -11
  64. package/dist/release-tags.d.ts +3 -1
  65. package/dist/release-tags.js +5 -2
  66. package/dist/signing.d.ts +64 -9
  67. package/dist/signing.js +113 -40
  68. package/dist/ssh-signature.d.ts +43 -0
  69. package/dist/ssh-signature.js +156 -0
  70. package/dist/summary.d.ts +3 -3
  71. package/dist/summary.js +2 -2
  72. package/dist/tag-format.d.ts +23 -14
  73. package/dist/tag-format.js +26 -40
  74. package/dist/version-files.d.ts +8 -8
  75. package/dist/version-files.js +22 -18
  76. package/dist/versions.d.ts +28 -3
  77. package/dist/versions.js +84 -9
  78. package/package.json +2 -2
@@ -29,8 +29,14 @@ declare const _default: {
29
29
  readonly 'no-dependents': {
30
30
  readonly type: "boolean";
31
31
  };
32
+ readonly prerelease: {
33
+ readonly type: "string";
34
+ };
35
+ readonly snapshot: {
36
+ readonly type: "string";
37
+ };
32
38
  };
33
- run({ values, cwd, configFile, logger }: import("./command.ts").CommandContext): Promise<{
39
+ run({ values, cwd, configFile, tagFormat, logger }: import("./command.ts").CommandContext): Promise<{
34
40
  exitCode: number;
35
41
  result: {
36
42
  date: string;
@@ -3,24 +3,37 @@ import { getChangesetVersionSummary } from "../summary.js";
3
3
  import { parseChangesetVersionArgs, changesetVersionArgs, } from "./changeset-args.js";
4
4
  export default {
5
5
  name: 'changeset-version',
6
- usage: 'changeset-version [--dry-run] [--date <YYYY-MM-DD>] [--package <name>] [--no-dependents]',
6
+ usage: 'changeset-version [--dry-run] [--date <YYYY-MM-DD>] [--package <name>] [--no-dependents] [--prerelease <id> | --snapshot <id>]',
7
7
  summary: 'Apply pending version bumps and changelog entries.',
8
8
  help: `Options:
9
9
  --dry-run Print the release plan without writing files
10
10
  --date <YYYY-MM-DD> Changelog date (default: today)
11
- --package <name> Version only this package (repeatable)
11
+ --package <name> Version only this package, by identifier or path
12
+ under the configured root (repeatable)
12
13
  --no-dependents Leave the configured dependents of the selected sources out
14
+ --prerelease <id> Release pre-releases like 1.3.0-rc.1, with an identifier
15
+ like rc (numbered from the tags) or rc.1
16
+ --snapshot <id> Release snapshots like 1.3.0-pr-251.20261012183000,
17
+ with an identifier like pr-251 and the UTC time
13
18
 
14
19
  Updates configured version files, prepends changelog entries and removes the
15
20
  applied changeset files. With --package, a changeset that also names other
16
21
  packages keeps only their entries, so it still applies to their releases, and
17
- changesets without packages are left for a run without --package.`,
22
+ changesets without packages are left for a run without --package.
23
+
24
+ With --prerelease, run it on a branch that isn't merged and tag that branch,
25
+ since the pre-release consumes the changesets that the stable release needs.
26
+ The readme Stable tag keeps the last stable version.
27
+
28
+ With --snapshot, build and publish the versioned files without committing
29
+ them, for example to test a pull request.`,
18
30
  options: changesetVersionArgs,
19
- async run({ values, cwd, configFile, logger }) {
31
+ async run({ values, cwd, configFile, tagFormat, logger }) {
20
32
  const args = parseChangesetVersionArgs(values);
21
33
  const result = await versionChangesets({
22
34
  cwd,
23
35
  ...(configFile ? { configFile } : {}),
36
+ ...(tagFormat ? { tagFormat } : {}),
24
37
  ...args,
25
38
  });
26
39
  result.releases.forEach((release) => logger.info(`${release.package.name}: ${release.currentVersion} → ${release.nextVersion} (${release.bump})`));
@@ -1,10 +1,10 @@
1
1
  import { basename, resolve } from 'node:path';
2
2
  import { CHECKSUMS_EXTENSION, createChecksums, writeChecksums, } from "../checksums.js";
3
- import { loadFileRules, resolveFileSelection } from "../file-selection.js";
3
+ import { ignoreOwnFile, ignoreOwnSignature, loadFileRules, resolveFileSelection, } from "../file-selection.js";
4
4
  import { FILENAME_FORMAT, formatFilename } from "../filename.js";
5
5
  import { readPackageMeta } from "../package-meta.js";
6
6
  import { getChecksumsSummary } from "../summary.js";
7
- import { checkDirectory, fileSelectionArgs, getFileSelectionInput, getSourceDirArg, ignoreOwnFile, ignoreOwnSignature, resolveOutput, sourceDirArgs, } from "./args.js";
7
+ import { checkDirectory, fileSelectionArgs, getFileSelectionInput, getSourceDirArg, resolveOutput, sourceDirArgs, } from "./args.js";
8
8
  import { getBoolean, getString } from "./command.js";
9
9
  export default {
10
10
  name: 'checksums',
@@ -1,4 +1,5 @@
1
1
  import type { ParseArgsOptionsConfig } from 'node:util';
2
+ import type { PromptTerminal } from '../changeset-prompt.ts';
2
3
  import type { Logger } from '../logger.ts';
3
4
  /**
4
5
  * Parsed option values from node:util parseArgs().
@@ -13,8 +14,12 @@ export interface CommandContext {
13
14
  cwd: string;
14
15
  /** Explicit JavaScript release config path from --config, if supplied. */
15
16
  configFile?: string;
17
+ /** Tag format from --tag-format, which replaces the tagFormat of the config. */
18
+ tagFormat?: string;
16
19
  /** Logger for the command output. */
17
20
  logger: Logger;
21
+ /** Terminal for interactive prompts, only when the CLI runs in one and prints no JSON. */
22
+ terminal?: PromptTerminal;
18
23
  }
19
24
  export interface CommandOutcome<Result = unknown> {
20
25
  /** Process exit code. */
@@ -15,12 +15,22 @@ declare const _default: {
15
15
  summary: string;
16
16
  help: string;
17
17
  options: {
18
- message: {
19
- type: "string";
20
- };
21
18
  'dry-run': {
22
19
  type: "boolean";
23
20
  };
21
+ 'release-ref': {
22
+ readonly type: "string";
23
+ };
24
+ package: {
25
+ readonly type: "string";
26
+ readonly multiple: true;
27
+ };
28
+ list: {
29
+ readonly type: "boolean";
30
+ };
31
+ message: {
32
+ type: "string";
33
+ };
24
34
  'git-url': {
25
35
  type: "string";
26
36
  };
@@ -37,7 +47,17 @@ declare const _default: {
37
47
  type: "string";
38
48
  };
39
49
  };
40
- run({ values, positionals, cwd, logger }: import("./command.ts").CommandContext): Promise<{
50
+ run({ values, positionals, cwd, configFile, tagFormat, logger }: import("./command.ts").CommandContext): Promise<{
51
+ exitCode: number;
52
+ result: {
53
+ packages: import("../package-deploy.ts").PackageDeployPlanItem[];
54
+ };
55
+ } | {
56
+ exitCode: number;
57
+ result: {
58
+ deploys: import("./package-deploys.ts").PackageDeployOutcome<import("../git-deploy.ts").GitDeployResult>[];
59
+ };
60
+ } | {
41
61
  exitCode: number;
42
62
  result: import("../git-deploy.ts").GitDeployResult;
43
63
  }>;
@@ -1,6 +1,22 @@
1
1
  import { GitDeploy, resolveGitDeployOptions } from "../git-deploy.js";
2
+ import { getGitDeploys } from "../package-deploy.js";
2
3
  import { fileSelectionArgs, getFileSelectionInput, getSourceDirArg, sourceDirArgs, } from "./args.js";
3
4
  import { getBoolean, getString } from "./command.js";
5
+ import { getPackageDeployArgs, listPackageDeploys, packageDeployArgs, runPackageDeploys, } from "./package-deploys.js";
6
+ /** Options of a single deploy target, which --release-ref takes from the config. */
7
+ const TARGET_FLAGS = [
8
+ 'source-dir',
9
+ 'git-url',
10
+ 'branch',
11
+ 'tag',
12
+ 'split-prefix',
13
+ 'source-ref',
14
+ 'distignore',
15
+ 'no-distignore',
16
+ 'exclude',
17
+ 'exclude-gitattributes',
18
+ 'message',
19
+ ];
4
20
  /**
5
21
  * Map the parsed "deploy-git" arguments to resolveGitDeployOptions() input.
6
22
  *
@@ -25,7 +41,7 @@ export function getGitDeployInputFromArgs(values, positionals, cwd) {
25
41
  }
26
42
  export default {
27
43
  name: 'deploy-git',
28
- usage: 'deploy-git [<source-dir>] --git-url <url> [options]',
44
+ usage: 'deploy-git [<source-dir>] --git-url <url> [options] | deploy-git --release-ref <ref> [--package <name>] [--list] [--dry-run]',
29
45
  summary: 'Deploy the files of a plugin, theme or package to a Git repository as a release commit.',
30
46
  help: `Arguments:
31
47
  <source-dir> Directory with the files to release (default: .),
@@ -37,6 +53,17 @@ Writes (none by default):
37
53
  --tag <name> Create this tag for the release, on top of --branch
38
54
  or the default branch of the repository
39
55
 
56
+ Configured packages (instead of the options above):
57
+ --release-ref <ref> Deploy the packages that this ref releases to the Git
58
+ targets in the config: the package of a release tag,
59
+ like refs/tags/plugins/alpha/1.2.0, or every package
60
+ for a branch, like refs/heads/main
61
+ --package <name> Deploy only this package (repeatable), for example
62
+ in a CI job for each package or to retry one
63
+ --list List the deploys without running them, with the
64
+ deploys of each package as the result for --json
65
+ and --github-output, for a CI matrix
66
+
40
67
  Options:
41
68
  --git-url <url> Repository to deploy to
42
69
  --split-prefix <dir> Use the history of this directory of the source
@@ -56,9 +83,21 @@ branch only moves forward, a release without changes isn't pushed again, an
56
83
  older build doesn't move a branch that has newer source history, a tag that
57
84
  already has the same files is reused, and a tag with other files
58
85
  fails with exit code 2 (delete it in the repository to release it again). The source directory must be inside a Git repository.
59
- Credentials come from git, like a token in the URL or an SSH key.
86
+ Credentials come from git, like a credential helper or an SSH key. For an
87
+ HTTPS repository on github.com, or on the host of GITHUB_SERVER_URL, the
88
+ GITHUB_TOKEN environment variable is sent as an authorization header, so the
89
+ token stays out of the URL.
90
+
91
+ With --release-ref, each package deploys to every Git target of its
92
+ deploy.git config, with the history of its directory, in one log group for
93
+ each package, and a failed deploy doesn't stop the others but exits with 1. A release tag creates the tag of each target on top
94
+ of its default branch, and a branch releases to the same branch.
60
95
 
61
96
  Examples:
97
+ wp-release deploy-git --release-ref "$GITHUB_REF"
98
+ Deploy the configured packages of a tag or branch push
99
+ wp-release deploy-git --release-ref refs/heads/main --list
100
+ Preview what a push to main deploys
62
101
  wp-release deploy-git build --git-url git@github.com:me/my-plugin-dist.git --branch main --tag 1.2.0
63
102
  Release a build to a distribution repository and tag it
64
103
  wp-release deploy-git packages/my-plugin --split-prefix packages/my-plugin --git-url … --branch main
@@ -72,9 +111,24 @@ Examples:
72
111
  'source-ref': { type: 'string' },
73
112
  ...fileSelectionArgs,
74
113
  message: { type: 'string' },
114
+ ...packageDeployArgs,
75
115
  'dry-run': { type: 'boolean' },
76
116
  },
77
- async run({ values, positionals, cwd, logger }) {
117
+ async run({ values, positionals, cwd, configFile, tagFormat, logger }) {
118
+ const { ref, packages, list } = getPackageDeployArgs(values, positionals, TARGET_FLAGS);
119
+ if (ref) {
120
+ const deploys = await getGitDeploys({
121
+ cwd,
122
+ ref,
123
+ packages,
124
+ dryRun: getBoolean(values, 'dry-run'),
125
+ ...(configFile ? { configFile } : {}),
126
+ ...(tagFormat ? { tagFormat } : {}),
127
+ });
128
+ return list
129
+ ? listPackageDeploys(deploys, logger, ref)
130
+ : runPackageDeploys(deploys, (input) => new GitDeploy(resolveGitDeployOptions(input), logger).run(), logger, ref);
131
+ }
78
132
  const options = resolveGitDeployOptions(getGitDeployInputFromArgs(values, positionals, cwd));
79
133
  const result = await new GitDeploy(options, logger).run();
80
134
  return { exitCode: 0, result };
@@ -23,6 +23,19 @@ declare const _default: {
23
23
  summary: string;
24
24
  help: string;
25
25
  options: {
26
+ 'dry-run': {
27
+ type: "boolean";
28
+ };
29
+ 'release-ref': {
30
+ readonly type: "string";
31
+ };
32
+ package: {
33
+ readonly type: "string";
34
+ readonly multiple: true;
35
+ };
36
+ list: {
37
+ readonly type: "boolean";
38
+ };
26
39
  'svn-url': {
27
40
  type: "string";
28
41
  };
@@ -35,9 +48,6 @@ declare const _default: {
35
48
  force: {
36
49
  type: "boolean";
37
50
  };
38
- 'dry-run': {
39
- type: "boolean";
40
- };
41
51
  trunk: {
42
52
  type: "boolean";
43
53
  };
@@ -66,7 +76,17 @@ declare const _default: {
66
76
  type: "string";
67
77
  };
68
78
  };
69
- run({ values, positionals, cwd, logger }: import("./command.ts").CommandContext): Promise<{
79
+ run({ values, positionals, cwd, configFile, tagFormat, logger }: import("./command.ts").CommandContext): Promise<{
80
+ exitCode: number;
81
+ result: {
82
+ packages: import("../package-deploy.ts").PackageDeployPlanItem[];
83
+ };
84
+ } | {
85
+ exitCode: number;
86
+ result: {
87
+ deploys: import("./package-deploys.ts").PackageDeployOutcome<import("../svn-deploy.ts").DeployResult>[];
88
+ };
89
+ } | {
70
90
  exitCode: number;
71
91
  result: import("../svn-deploy.ts").DeployResult;
72
92
  }>;
@@ -1,7 +1,28 @@
1
1
  import { resolveDeployOptions } from "../deploy-options.js";
2
+ import { getSvnDeploys } from "../package-deploy.js";
2
3
  import { SvnDeploy } from "../svn-deploy.js";
3
4
  import { fileSelectionArgs, getFileSelectionInput, getSourceDirArg, sourceDirArgs, } from "./args.js";
4
5
  import { getBoolean, getString } from "./command.js";
6
+ import { getPackageDeployArgs, listPackageDeploys, packageDeployArgs, runPackageDeploys, } from "./package-deploys.js";
7
+ /** Options of a single deploy, which --release-ref takes from the config. */
8
+ const TARGET_FLAGS = [
9
+ 'source-dir',
10
+ 'trunk',
11
+ 'readme-only',
12
+ 'svn-tag',
13
+ 'assets-dir',
14
+ 'no-assets',
15
+ 'slug',
16
+ 'version',
17
+ 'readme',
18
+ 'from-zip',
19
+ 'distignore',
20
+ 'no-distignore',
21
+ 'exclude',
22
+ 'exclude-gitattributes',
23
+ 'svn-url',
24
+ 'message',
25
+ ];
5
26
  /**
6
27
  * Map the parsed "deploy-svn" arguments to resolveDeployOptions() input.
7
28
  * Flags that aren't passed stay undefined, so that the same defaults apply
@@ -35,7 +56,7 @@ export function getDeployInputFromArgs(input) {
35
56
  }
36
57
  export default {
37
58
  name: 'deploy-svn',
38
- usage: 'deploy-svn [<source-dir>] [options]',
59
+ usage: 'deploy-svn [<source-dir>] [options] | deploy-svn --release-ref <ref> [--package <name>] [--list] [--dry-run]',
39
60
  summary: 'Deploy a plugin to the WordPress.org plugin SVN repository.',
40
61
  help: `Arguments:
41
62
  <source-dir> Directory with the plugin files (default: .), also
@@ -51,6 +72,15 @@ Writes (none by default, assets only with --assets-dir):
51
72
  screenshots to assets/
52
73
  --no-assets Don't sync --assets-dir for this run
53
74
 
75
+ Configured packages (instead of the writes and the source options):
76
+ --release-ref <ref> Deploy the package that this release tag releases,
77
+ like refs/tags/plugins/alpha/1.2.0, to the SVN
78
+ targets in the config. Branches aren't deployed.
79
+ --package <name> Deploy only this package (repeatable)
80
+ --list List the deploys without running them, with the
81
+ deploys of each package as the result for --json
82
+ and --github-output, for a CI matrix
83
+
54
84
  Options:
55
85
  --slug <slug> Plugin slug (default: project directory name)
56
86
  --version <version> Version to check (default: --svn-tag, then the Version header)
@@ -71,7 +101,13 @@ Options:
71
101
  Credentials are only needed to commit. Exits with 3 when they're missing
72
102
  or rejected.
73
103
 
104
+ With --release-ref, a stable release syncs trunk/, creates its tag and syncs
105
+ the assetsDir of each deploy.svn target, while a pre-release only creates its
106
+ tag. A failed deploy doesn't stop the others and exits with 1.
107
+
74
108
  Examples:
109
+ wp-release deploy-svn --release-ref "$GITHUB_REF"
110
+ Deploy the configured plugin of a release tag push
75
111
  wp-release deploy-svn dist --trunk --svn-tag 1.2.0 --assets-dir .wordpress-org
76
112
  Deploy 1.2.0 to trunk and the tag, and update the assets
77
113
  wp-release deploy-svn dist --svn-tag 1.2.0-rc.1
@@ -96,9 +132,28 @@ Examples:
96
132
  'svn-username': { type: 'string' },
97
133
  message: { type: 'string' },
98
134
  force: { type: 'boolean' },
135
+ ...packageDeployArgs,
99
136
  'dry-run': { type: 'boolean' },
100
137
  },
101
- async run({ values, positionals, cwd, logger }) {
138
+ async run({ values, positionals, cwd, configFile, tagFormat, logger }) {
139
+ const { ref, packages, list } = getPackageDeployArgs(values, positionals, TARGET_FLAGS);
140
+ if (ref) {
141
+ const deploys = await getSvnDeploys({
142
+ cwd,
143
+ ref,
144
+ packages,
145
+ dryRun: getBoolean(values, 'dry-run'),
146
+ ...(configFile ? { configFile } : {}),
147
+ ...(tagFormat ? { tagFormat } : {}),
148
+ });
149
+ return list
150
+ ? listPackageDeploys(deploys, logger, ref)
151
+ : runPackageDeploys(deploys, (input) => new SvnDeploy(resolveDeployOptions({
152
+ ...input,
153
+ svnUsername: getString(values, 'svn-username'),
154
+ force: getBoolean(values, 'force'),
155
+ }), logger).run(), logger, ref);
156
+ }
102
157
  const options = resolveDeployOptions(getDeployInputFromArgs({ values, positionals, cwd }));
103
158
  const result = await new SvnDeploy(options, logger).run();
104
159
  return { exitCode: 0, result };
@@ -1,3 +1,4 @@
1
+ import type { SignatureFormat } from '../signing.ts';
1
2
  import type { CommandOutcome } from './command.ts';
2
3
  /** Generated Ed25519 signing keys. */
3
4
  export interface KeygenResult {
@@ -5,6 +6,8 @@ export interface KeygenResult {
5
6
  privateKey: string;
6
7
  /** Base64 Ed25519 public key for verification. */
7
8
  publicKey: string;
9
+ /** Public key in the form of each signature format. */
10
+ publicKeys: Record<SignatureFormat, string>;
8
11
  }
9
12
  declare const _default: {
10
13
  name: string;
@@ -8,7 +8,10 @@ export default {
8
8
  help: `Generate a signing key pair for "wp-release sign" and "wp-release verify".
9
9
 
10
10
  The private seed is printed once as WP_RELEASE_SIGNING_KEY. Store it securely
11
- and never commit it or share it. The public key can be distributed to verifiers.`,
11
+ and never commit it or share it. The public key can be distributed to verifiers.
12
+ It's also printed as a minisign public key for "minisign -V -P" and as an
13
+ ssh-ed25519 public key for the allowed_signers file of "ssh-keygen -Y verify",
14
+ since the same key signs every format of "wp-release sign".`,
12
15
  options: {},
13
16
  async run({ values, logger }) {
14
17
  if (getBoolean(values, 'github-output')) {
@@ -17,6 +20,8 @@ and never commit it or share it. The public key can be distributed to verifiers.
17
20
  const result = generateSigningKeyPair();
18
21
  logger.info(`WP_RELEASE_SIGNING_KEY=${result.privateKey}`);
19
22
  logger.info(`Public key: ${result.publicKey}`);
23
+ logger.info(`Minisign public key: ${result.publicKeys.minisign}`);
24
+ logger.info(`SSH public key: ${result.publicKeys.ssh}`);
20
25
  logger.info('Store the private seed securely; it cannot be recovered from the public key.');
21
26
  return { exitCode: 0, result };
22
27
  },
@@ -0,0 +1,86 @@
1
+ import type { Logger } from '../logger.ts';
2
+ import type { PackageDeploy, PackageDeployPlanItem } from '../package-deploy.ts';
3
+ import type { ParsedValues } from './command.ts';
4
+ /**
5
+ * Options of the deploy commands that deploy the configured packages of a
6
+ * ref, in node:util parseArgs() format.
7
+ */
8
+ export declare const packageDeployArgs: {
9
+ readonly 'release-ref': {
10
+ readonly type: "string";
11
+ };
12
+ readonly package: {
13
+ readonly type: "string";
14
+ readonly multiple: true;
15
+ };
16
+ readonly list: {
17
+ readonly type: "boolean";
18
+ };
19
+ };
20
+ /** Result of one deploy of a configured package. */
21
+ export interface PackageDeployOutcome<Result> {
22
+ package: string;
23
+ /** Package, target and ref, with credentials redacted. */
24
+ label: string;
25
+ /** Repository or WordPress.org plugin of the target, with credentials redacted. */
26
+ target: string;
27
+ /** Tag or branch that the deploy writes in the target. */
28
+ targetRef: string;
29
+ /** Host of a remote target repository, like github.com. */
30
+ targetHost?: string;
31
+ /** Path of a remote target repository on its host, like owner/repo. */
32
+ targetPath?: string;
33
+ version?: string;
34
+ branch?: string;
35
+ /** Deploy result, unless it failed. */
36
+ result?: Result;
37
+ /** Error message of a failed deploy. */
38
+ error?: string;
39
+ }
40
+ /**
41
+ * Get the ref and the packages to deploy from the config, failing for the
42
+ * options that set a single deploy target instead.
43
+ *
44
+ * @param values Parsed CLI values.
45
+ * @param positionals Positional arguments.
46
+ * @param targetFlags Options of a single deploy target.
47
+ * @return The ref and the selected packages.
48
+ * @throws {ValidationError} For --package without --release-ref, or a single target option with it.
49
+ */
50
+ export declare function getPackageDeployArgs(values: ParsedValues, positionals: string[], targetFlags: string[]): {
51
+ ref?: string;
52
+ packages: string[];
53
+ list: boolean;
54
+ };
55
+ /**
56
+ * Run the deploys of the configured packages one at a time, in one log group
57
+ * for each package, continuing after a failed deploy so that one package or
58
+ * target doesn't block the others.
59
+ *
60
+ * @param deploys Deploys from getGitDeploys() or getSvnDeploys().
61
+ * @param deploy Runs one deploy.
62
+ * @param logger Logger.
63
+ * @param ref Deployed ref, for the log.
64
+ * @return Exit code 1 when a deploy failed, and the result of each deploy.
65
+ */
66
+ export declare function runPackageDeploys<Input, Result>(deploys: PackageDeploy<Input>[], deploy: (input: Input) => Promise<Result>, logger: Logger, ref: string): Promise<{
67
+ exitCode: number;
68
+ result: {
69
+ deploys: PackageDeployOutcome<Result>[];
70
+ };
71
+ }>;
72
+ /**
73
+ * List the deploys of the configured packages without running them, one line
74
+ * for each deploy, with the plan as the result for --json and --github-output.
75
+ *
76
+ * @param deploys Deploys from getGitDeploys() or getSvnDeploys().
77
+ * @param logger Logger.
78
+ * @param ref Deployed ref, for the log.
79
+ * @return Exit code 0 and the deploys of each package.
80
+ */
81
+ export declare function listPackageDeploys(deploys: PackageDeploy<unknown>[], logger: Logger, ref: string): {
82
+ exitCode: number;
83
+ result: {
84
+ packages: PackageDeployPlanItem[];
85
+ };
86
+ };
@@ -0,0 +1,113 @@
1
+ import { ValidationError } from "../errors.js";
2
+ import { getDeployPlan } from "../package-deploy.js";
3
+ import { logError } from "../report.js";
4
+ import { getBoolean, getString, getStrings } from "./command.js";
5
+ /**
6
+ * Options of the deploy commands that deploy the configured packages of a
7
+ * ref, in node:util parseArgs() format.
8
+ */
9
+ export const packageDeployArgs = {
10
+ 'release-ref': { type: 'string' },
11
+ package: { type: 'string', multiple: true },
12
+ list: { type: 'boolean' },
13
+ };
14
+ /**
15
+ * Get the ref and the packages to deploy from the config, failing for the
16
+ * options that set a single deploy target instead.
17
+ *
18
+ * @param values Parsed CLI values.
19
+ * @param positionals Positional arguments.
20
+ * @param targetFlags Options of a single deploy target.
21
+ * @return The ref and the selected packages.
22
+ * @throws {ValidationError} For --package without --release-ref, or a single target option with it.
23
+ */
24
+ export function getPackageDeployArgs(values, positionals, targetFlags) {
25
+ const ref = getString(values, 'release-ref')?.trim();
26
+ const packages = getStrings(values, 'package');
27
+ const list = getBoolean(values, 'list');
28
+ const conflicts = targetFlags.filter((flag) => undefined !== getString(values, flag) ||
29
+ getBoolean(values, flag) ||
30
+ getStrings(values, flag).length);
31
+ if ('' === ref) {
32
+ throw new ValidationError('--release-ref <ref> must not be empty.');
33
+ }
34
+ if ((packages.length || list) && !ref) {
35
+ throw new ValidationError(`${list ? '--list' : '--package'} requires --release-ref <ref>.`);
36
+ }
37
+ if (ref && (positionals.length || conflicts.length)) {
38
+ throw new ValidationError(`--release-ref deploys the configured packages and targets, so it can't be combined with ${positionals.length
39
+ ? 'a source directory'
40
+ : conflicts.map((flag) => `--${flag}`).join(', ')}.`);
41
+ }
42
+ return { ...(ref ? { ref } : {}), packages, list };
43
+ }
44
+ /**
45
+ * Run the deploys of the configured packages one at a time, in one log group
46
+ * for each package, continuing after a failed deploy so that one package or
47
+ * target doesn't block the others.
48
+ *
49
+ * @param deploys Deploys from getGitDeploys() or getSvnDeploys().
50
+ * @param deploy Runs one deploy.
51
+ * @param logger Logger.
52
+ * @param ref Deployed ref, for the log.
53
+ * @return Exit code 1 when a deploy failed, and the result of each deploy.
54
+ */
55
+ export async function runPackageDeploys(deploys, deploy, logger, ref) {
56
+ const outcomes = [];
57
+ const names = [
58
+ ...new Set(deploys.map((item) => item.package.name)),
59
+ ];
60
+ if (!deploys.length) {
61
+ logger.info(`No configured package deploys ${ref}.`);
62
+ }
63
+ for (const name of names) {
64
+ const items = deploys.filter((item) => item.package.name === name);
65
+ const release = items[0]?.release ?? {};
66
+ await logger.group(`${name} ${release.version ?? release.branch ?? ''}`.trim(), async () => {
67
+ for (const item of items) {
68
+ outcomes.push(await runDeploy(item, deploy, logger));
69
+ }
70
+ });
71
+ }
72
+ return {
73
+ exitCode: outcomes.some((outcome) => outcome.error) ? 1 : 0,
74
+ result: { deploys: outcomes },
75
+ };
76
+ }
77
+ async function runDeploy(item, deploy, logger) {
78
+ const outcome = {
79
+ package: item.package.name,
80
+ label: item.label,
81
+ target: item.target,
82
+ targetRef: item.targetRef,
83
+ ...(item.targetHost
84
+ ? { targetHost: item.targetHost, targetPath: item.targetPath }
85
+ : {}),
86
+ ...item.release,
87
+ };
88
+ try {
89
+ outcome.result = await deploy(item.input);
90
+ }
91
+ catch (error) {
92
+ outcome.error =
93
+ error instanceof Error ? error.message : String(error);
94
+ logError(logger, error, `Deploy of ${item.label}`);
95
+ }
96
+ return outcome;
97
+ }
98
+ /**
99
+ * List the deploys of the configured packages without running them, one line
100
+ * for each deploy, with the plan as the result for --json and --github-output.
101
+ *
102
+ * @param deploys Deploys from getGitDeploys() or getSvnDeploys().
103
+ * @param logger Logger.
104
+ * @param ref Deployed ref, for the log.
105
+ * @return Exit code 0 and the deploys of each package.
106
+ */
107
+ export function listPackageDeploys(deploys, logger, ref) {
108
+ if (!deploys.length) {
109
+ logger.info(`No configured package deploys ${ref}.`);
110
+ }
111
+ deploys.forEach((item) => logger.info(item.label));
112
+ return { exitCode: 0, result: { packages: getDeployPlan(deploys) } };
113
+ }
@@ -1,17 +1,35 @@
1
+ import type { SignatureFormat } from '../signing.ts';
1
2
  import type { CommandOutcome } from './command.ts';
2
3
  /** Result of the sign command. */
3
4
  export interface SignResult {
4
- /** Absolute paths of the written .sig files. */
5
+ /** Absolute paths of the written signature files. */
5
6
  signatures: string[];
7
+ /** Signature formats that were written. */
8
+ formats: SignatureFormat[];
6
9
  /** Base64 public key of the signing key. */
7
10
  publicKey: string;
11
+ /** Public key of the signing key in the form of each signature format. */
12
+ publicKeys: Record<SignatureFormat, string>;
8
13
  }
14
+ /**
15
+ * Get the signature formats from the --format values, which can be repeated
16
+ * or comma-separated.
17
+ *
18
+ * @param values Option values.
19
+ * @return Unique formats, the default when none are given.
20
+ * @throws {ValidationError} If a format is unknown.
21
+ */
22
+ export declare function parseSignatureFormats(values: string[]): SignatureFormat[];
9
23
  declare const _default: {
10
24
  name: string;
11
25
  usage: string;
12
26
  summary: string;
13
27
  help: string;
14
28
  options: {
29
+ format: {
30
+ type: "string";
31
+ multiple: true;
32
+ };
15
33
  force: {
16
34
  type: "boolean";
17
35
  };