@wpelevator/wp-release 0.14.0 → 0.16.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.
- package/README.md +256 -36
- package/dist/changeset-config.d.ts +3 -0
- package/dist/changeset-release.d.ts +9 -3
- package/dist/changeset-release.js +23 -14
- package/dist/cli.d.ts +17 -7
- package/dist/cli.js +27 -2
- package/dist/commands/args.d.ts +11 -0
- package/dist/commands/args.js +23 -0
- package/dist/commands/artifact-signing.d.ts +20 -0
- package/dist/commands/artifact-signing.js +42 -0
- package/dist/commands/build.d.ts +32 -0
- package/dist/commands/build.js +91 -0
- package/dist/commands/changeset-add.d.ts +55 -6
- package/dist/commands/changeset-add.js +12 -3
- package/dist/commands/changeset-args.js +2 -2
- package/dist/commands/changeset-project-args.d.ts +63 -0
- package/dist/commands/changeset-project-args.js +114 -0
- package/dist/commands/changeset-status.d.ts +50 -1
- package/dist/commands/changeset-status.js +4 -2
- package/dist/commands/changeset-tag.d.ts +55 -6
- package/dist/commands/changeset-tag.js +7 -4
- package/dist/commands/changeset-version.d.ts +55 -6
- package/dist/commands/changeset-version.js +4 -2
- package/dist/commands/checksums.d.ts +4 -1
- package/dist/commands/checksums.js +27 -6
- package/dist/commands/command.d.ts +3 -0
- package/dist/commands/deploy-svn.d.ts +11 -15
- package/dist/commands/deploy-svn.js +25 -5
- package/dist/commands/package-source.d.ts +43 -0
- package/dist/commands/package-source.js +101 -0
- package/dist/commands/sign.d.ts +0 -9
- package/dist/commands/sign.js +10 -47
- package/dist/commands/verify.d.ts +7 -1
- package/dist/commands/verify.js +53 -17
- package/dist/commands/zip.d.ts +25 -2
- package/dist/commands/zip.js +136 -110
- package/dist/index.d.ts +9 -2
- package/dist/index.js +3 -1
- package/dist/integrity.d.ts +33 -0
- package/dist/integrity.js +115 -0
- package/dist/output-files.d.ts +7 -0
- package/dist/output-files.js +25 -0
- package/dist/package-build.d.ts +33 -0
- package/dist/package-build.js +112 -0
- package/dist/package-files.d.ts +25 -0
- package/dist/package-files.js +24 -0
- package/dist/package-zip.d.ts +14 -2
- package/dist/package-zip.js +129 -38
- package/dist/release-tags.js +6 -0
- package/dist/signing.d.ts +36 -0
- package/dist/signing.js +52 -1
- package/dist/single-project.d.ts +32 -0
- package/dist/single-project.js +144 -0
- package/dist/summary.d.ts +9 -0
- package/dist/summary.js +25 -0
- package/dist/zip.d.ts +3 -0
- package/dist/zip.js +16 -6
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# WP Release
|
|
2
2
|
|
|
3
|
-
Package, checksum, verify and version-check WordPress plugins and themes, deploy plugins to the WordPress.org plugin SVN repository, and deploy plugins, themes and packages to Git repositories. Usable as a CLI, as a library, and through the [`wp-release-zip`](https://github.com/wpelevator/wp-release-zip), [`wp-release-checksums`](https://github.com/wpelevator/wp-release-checksums), [`wp-release-sign`](https://github.com/wpelevator/wp-release-sign) and [`wp-release-deploy-svn`](https://github.com/wpelevator/wp-release-deploy-svn) GitHub Actions.
|
|
3
|
+
Package, checksum, sign, verify and version-check WordPress plugins and themes, deploy plugins to the WordPress.org plugin SVN repository, and deploy plugins, themes and packages to Git repositories. Usable as a CLI, as a library, and through the [`wp-release-zip`](https://github.com/wpelevator/wp-release-zip), [`wp-release-checksums`](https://github.com/wpelevator/wp-release-checksums), [`wp-release-sign`](https://github.com/wpelevator/wp-release-sign) and [`wp-release-deploy-svn`](https://github.com/wpelevator/wp-release-deploy-svn) GitHub Actions.
|
|
4
4
|
|
|
5
5
|
Themes are released by uploading a ZIP on WordPress.org, so `deploy-svn` is for plugins only; use `zip` to build the theme upload.
|
|
6
6
|
|
|
7
|
-
**Status:** first release (0.x). Signatures are tested against libsodium, but haven't been used for a real release yet.
|
|
8
|
-
|
|
9
7
|
Option names follow the existing community tools (10up WordPress.org actions, `wp dist-archive`, `wp-scripts plugin-zip`, `grunt-wp-deploy`) so that migrating needs few or no renames. See [Comparison with existing tools](#comparison-with-existing-tools) for the differences.
|
|
10
8
|
|
|
11
9
|
## Install
|
|
@@ -14,7 +12,7 @@ Option names follow the existing community tools (10up WordPress.org actions, `w
|
|
|
14
12
|
npm install --save-dev @wpelevator/wp-release
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
Requires Node.js 22.12 or later. `deploy-svn` also needs the `svn` command line client
|
|
15
|
+
Requires Node.js 22.12 or later. `deploy-svn` also needs the `svn` command line client. Git deployments, changeset commands and the optional `--exclude-gitattributes` flag need Git.
|
|
18
16
|
|
|
19
17
|
The package is an ES module with TypeScript type definitions. CommonJS projects can load it with `require( '@wpelevator/wp-release' )`, which Node.js supports for ES modules without top-level `await` since 22.12.
|
|
20
18
|
|
|
@@ -26,6 +24,7 @@ npx wp-release --help
|
|
|
26
24
|
|
|
27
25
|
| Command | Description |
|
|
28
26
|
|---|---|
|
|
27
|
+
| `wp-release build [<source-dir>] --output <dir> [options]` | Copy selected release files into a directory. |
|
|
29
28
|
| `wp-release zip [<source-dir>] [options]` | Create a ZIP archive with a `{slug}/` root folder, respecting `.distignore`. |
|
|
30
29
|
| `wp-release checksums [<source-dir>] [options]` | Write the MD5 and SHA-256 hashes of every distributed file to a checksums manifest. |
|
|
31
30
|
| `wp-release sign <file>... [options]` | Sign files with Ed25519, writing a detached `<file>.sig` next to each, or minisign and SSH signatures with `--format`. |
|
|
@@ -41,7 +40,11 @@ npx wp-release --help
|
|
|
41
40
|
| `wp-release changeset-version [options]` | Apply version bumps and changelog entries. |
|
|
42
41
|
| `wp-release changeset-tag [options]` | Create missing package release tags. |
|
|
43
42
|
|
|
44
|
-
Run `npx wp-release <command> --help` for the options of each command.
|
|
43
|
+
Run `npx wp-release <command> --help` for the options of each command. Directory-based commands accept a first `<source-dir>` argument or `--source-dir`, defaulting to the current directory. `sign` takes file arguments, `keygen` generates keys, and changeset commands operate on the configured repository. Every command prints its result as JSON with `--json`.
|
|
44
|
+
|
|
45
|
+
In GitHub Actions, `--github-output` writes the complete result as JSON to `steps.<id>.outputs.result` and exposes each top-level string, number or boolean as a plain output. Use `steps.zip.outputs.zipPath` for a ZIP path, for example. Arrays and objects remain in `result`: tag metadata is read with `fromJSON( steps.tag.outputs.result ).tags[0].tag`, even when only one package is selected. There is no plain `steps.tag.outputs.tag` output.
|
|
46
|
+
|
|
47
|
+
Prefer plain outputs in steps that run conditionally, since a skipped step has empty outputs. When reading JSON from a step that may be skipped, use a fallback such as `fromJSON( steps.tag.outputs.result || '{"tags":[]}' ).tags[0].tag`. For several tags or deploys, read the arrays in `result` to build a job matrix or process each item.
|
|
45
48
|
|
|
46
49
|
### GitHub Actions annotations and job summaries
|
|
47
50
|
|
|
@@ -65,7 +68,8 @@ Each command also adds a section to the [job summary](https://docs.github.com/en
|
|
|
65
68
|
|---|---|
|
|
66
69
|
| `deploy-svn` | Plugin, linked to its WordPress.org page, version, written SVN paths, the commit linked to its WordPress.org changeset (or the dry run or no-op result), and the SVN changes in a collapsed table. |
|
|
67
70
|
| `deploy-git` | Repository, release commit and the pushed branch and tag. |
|
|
68
|
-
| `
|
|
71
|
+
| `build` | Output directory, number of copied files and optional manifest; requested signatures and public keys. |
|
|
72
|
+
| `zip` | ZIP, number of files and checksums manifest; with `--sign`, signature files and the public key of each written format. |
|
|
69
73
|
| `checksums` | Manifest and number of files. |
|
|
70
74
|
| `sign` | Signature files and the public key of each written format. |
|
|
71
75
|
| `verify` | That all files match, or the counts and a table of the changed, missing and extra files. |
|
|
@@ -76,11 +80,63 @@ Each command also adds a section to the [job summary](https://docs.github.com/en
|
|
|
76
80
|
|
|
77
81
|
A failed command adds its error to the summary, or a table of its problems with their files and lines, listing up to 100 and counting the rest. The Markdown of the summaries isn't part of the library API. File paths are relative to `GITHUB_WORKSPACE`, or the current directory without it. Files outside it, like those of an extracted ZIP, get an annotation without a file. Multi-line messages stay one annotation.
|
|
78
82
|
|
|
79
|
-
Use global `--config <path>` to select a config; relative paths resolve from the current directory. Otherwise,
|
|
83
|
+
Use global `--config <path>` to select a config; relative paths resolve from the current directory. Otherwise, commands using the config discover `wp-release.config.js`, `.mjs` or `.cjs` at the repository root. Global `--tag-format <format>` replaces the `tagFormat` of the config for one command, see [Packages and tags](#packages-and-tags).
|
|
84
|
+
|
|
85
|
+
### Changesets support
|
|
86
|
+
|
|
87
|
+
The changeset commands manage versioning, changelogs and release tags for single-package repositories and monorepos, including this repository. They read Changesets-style Markdown and load JavaScript configs with descriptors for npm and Composer manifests, plugin and theme headers, readme Stable tags, PHP constants, and Git tags. Use JavaScript configs with ESM (`export default`) or CommonJS (`module.exports`). Frontmatter uses `package-name: patch|minor|major` entries. For pre-releases, use the built-in [pre-release workflow](#pre-releases); the parser supports this simple frontmatter format rather than general YAML or the Changesets pre mode.
|
|
88
|
+
|
|
89
|
+
#### Single-project repositories without a config file
|
|
90
|
+
|
|
91
|
+
The `changeset-*` commands can release one project at the repository root without a `wp-release.config.*` file. Run them from the root directory. With no version descriptor flags, they detect a root `package.json` or `composer.json` with a version, a plugin entry PHP file with a `Plugin Name` header, or a theme `style.css` with a `Theme Name` header. A detected plugin also updates the Stable tag in `readme.txt`, or `readme.md` when no Stable tag is found in `readme.txt`. Multiple version sources, multiple plugin entry files and npm workspaces require explicit descriptors or a config; files belonging to different projects aren't silently synchronized. A directory without a recognized version source still requires a config or explicit descriptors.
|
|
92
|
+
|
|
93
|
+
For a root npm package, the complete workflow needs no project flags:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx wp-release changeset-add --bump minor --summary "Add a new feature."
|
|
97
|
+
npx wp-release changeset-status --since origin/main
|
|
98
|
+
npx wp-release changeset-version --dry-run
|
|
99
|
+
npx wp-release changeset-version
|
|
100
|
+
# Review and commit the updated version and changelog files.
|
|
101
|
+
npx wp-release changeset-tag --push
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
A single root package is selected automatically for `changeset-add --bump` when `--since` isn't supplied, and for `changeset-tag --version`; `--since` still selects changed packages. npm and Composer projects use their root manifest's name as the changeset identifier when that manifest supplies the version; other projects use the root directory's basename. Keep that directory name consistent between local checkouts and CI, or use a config with a manifest-based `idFrom`. The fallback uses `.changeset/`, `CHANGELOG.md`, tags named `v{version}`, and the `origin` remote. There is no default comparison branch: pass `--since` or `--base-branch` when status checks should compare changes. Readme changelog updates are opt-in.
|
|
105
|
+
|
|
106
|
+
Explicit descriptors replace automatic detection and define the entire version target list. For a plugin whose version appears in its header, readme and a PHP constant:
|
|
80
107
|
|
|
81
|
-
|
|
108
|
+
```bash
|
|
109
|
+
npx wp-release changeset-version \
|
|
110
|
+
--version-from wordpress-plugin \
|
|
111
|
+
--version-wordpress-plugin my-plugin.php \
|
|
112
|
+
--version-wordpress-stable-tag readme.txt \
|
|
113
|
+
--version-php-constant 'my-plugin.php#MY_PLUGIN_VERSION'
|
|
114
|
+
```
|
|
82
115
|
|
|
83
|
-
|
|
116
|
+
The same project flags belong on `changeset-add`, `changeset-status`, `changeset-version` and `changeset-tag`. Keep them consistent across all four commands, for example in command-specific npm scripts. An accompanying `package.json` used for build tools isn't changed unless it is included explicitly.
|
|
117
|
+
|
|
118
|
+
| Flag | Version descriptor |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `--version-package-json <file>` | npm manifest version source and target |
|
|
121
|
+
| `--version-composer-json <file>` | Composer manifest version source and target; a target without a `version` field is skipped, while a source must have one |
|
|
122
|
+
| `--version-wordpress-plugin <file>` | WordPress plugin Version header source and target |
|
|
123
|
+
| `--version-wordpress-theme <file>` | WordPress theme Version header source and target |
|
|
124
|
+
| `--version-wordpress-stable-tag <file>` | WordPress readme Stable tag source and target; keeps its stable version during pre-releases and snapshots |
|
|
125
|
+
| `--version-php-constant <file>#<constant>` | PHP `define()` or `const` version source and target |
|
|
126
|
+
| `--version-git-tag <pattern>` | Current version from existing stable Git tags, with a pattern such as `v{version}`; source-only, never a version target |
|
|
127
|
+
|
|
128
|
+
File descriptor flags are repeatable. The source is inferred from a sole descriptor, or a unique descriptor other than a Stable tag. Otherwise use `--version-from <kind>`, where kind is `package-json`, `composer-json`, `wordpress-plugin`, `wordpress-theme`, `wordpress-stable-tag`, `php-constant` or `git-tag`. For repeated kinds, qualify the selector with the exact descriptor value: `--version-from package-json:package.json` or `--version-from 'php-constant:my-plugin.php#MY_PLUGIN_VERSION'`. Argument order doesn't select the source. A Git source requires a matching existing stable tag; its pattern selects the tags to read, while `--tag-format` controls tags to create. With multiple descriptors, explicitly select `git-tag` if it is the authoritative source.
|
|
129
|
+
|
|
130
|
+
| Shared option | Config equivalent |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `--base-branch <branch>` | `baseBranch`, used with the remote for the default comparison ref |
|
|
133
|
+
| `--remote <name>` | `remote`, used for comparisons and pushing release tags |
|
|
134
|
+
| `--changesets-dir <dir>` | `changesets`, relative to the repository root |
|
|
135
|
+
| `--readme-changelog` / `--no-readme-changelog` | `readmeChangelog`, independent of updating the readme Stable tag |
|
|
136
|
+
| `--changelog-insert-after <marker>` | `changelogInsertAfter`; must exist in `CHANGELOG.md` and, with readme changelog updates, the plugin readme |
|
|
137
|
+
| `--tag-format <format>` | `tagFormat`, replacing the default `v{version}` |
|
|
138
|
+
|
|
139
|
+
An explicit `--config` must load successfully. Otherwise a discovered config takes precedence over the single-project fallback. Single-project descriptor and settings flags cannot be mixed with a config file; put those settings in the config instead. The existing global `--tag-format` override works in both modes. Config files remain the place for package layouts, dependents, custom version and changelog callbacks, deployment destinations and distribution settings. Packaging and deployment commands retain their existing config requirements.
|
|
84
140
|
|
|
85
141
|
Typical release flow, from a feature pull request to the publish workflows triggered by release tags:
|
|
86
142
|
|
|
@@ -127,6 +183,23 @@ export default defineConfig( {
|
|
|
127
183
|
|
|
128
184
|
`version.from` selects the current version; `version.to` lists release targets. Use `phpConstantVersion( { file, constant } )` to sync a PHP constant.
|
|
129
185
|
|
|
186
|
+
#### Single-project checks in GitHub Actions
|
|
187
|
+
|
|
188
|
+
There are no dedicated build or changeset wrapper actions. Install the CLI in your project and run it directly. A root npm package can check a pull request with:
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
- uses: actions/checkout@v7
|
|
192
|
+
with:
|
|
193
|
+
fetch-depth: 0
|
|
194
|
+
- uses: actions/setup-node@v6
|
|
195
|
+
with:
|
|
196
|
+
node-version: 24
|
|
197
|
+
- run: npm ci
|
|
198
|
+
- run: npx --no-install wp-release changeset-status --since origin/main
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
For a plugin with several version targets, put the same descriptor flags in npm scripts for all four changeset commands. Full Git history and tags are needed for comparisons, Git version sources and release notes. Keep the checkout directory name consistent when it supplies the changeset identifier. Directory artifacts use `npx --no-install wp-release build --output dist/my-plugin --github-output`; upload that directory with your artifact action. `build` writes manifest sidecars outside the directory and does not accept the ZIP-only embedding, codec or manifest-filename flags.
|
|
202
|
+
|
|
130
203
|
#### Packages and tags
|
|
131
204
|
|
|
132
205
|
The config describes the repository, so wp-release assumes no directory structure or naming convention of its own:
|
|
@@ -143,7 +216,7 @@ The config describes the repository, so wp-release assumes no directory structur
|
|
|
143
216
|
Deploy templates also have `{branch}`, `{ref}` and `{revision}` (see [Deploy configured packages](#deploy-configured-packages)), and any template can be a callback that receives the package, for names that the placeholders don't cover, like the last directory of the path.
|
|
144
217
|
- **`tagFormat`** names the release tags, `{path}/{version}` by default, like `plugins/alpha/1.2.0`. Empty placeholders leave no leading or repeated slashes in a tag, so a package at the root gets plain version tags, like `1.2.0`, from the same default. Deploy templates, like repository URLs, are filled in as they are. A `gitTagVersion()` version source reads the tags with its own `pattern`, also `{path}/{version}` by default, so set both when you change the format.
|
|
145
218
|
- **`--tag-format <format>`** replaces the `tagFormat` of the config for one command, for a workflow or job that names its tags differently, like `--tag-format '{name}@{version}'`. Every command that creates or reads the same tags needs the same format: `changeset-tag`, which creates them and finds the previous tag for the release notes, `changeset-version --prerelease`, which numbers pre-releases from them, and `deploy-git` and `deploy-svn --release-ref`, which read the package and version of a tag.
|
|
146
|
-
- **Slugs** are a WordPress concept: the folder of a plugin or theme. Only the commands that produce something for WordPress use one: the `{slug}/` root folder and name of a `zip`, the `checksums` manifest, the WordPress.org slug of `deploy-svn`, and `version-check`. Each takes `--slug`, which defaults to the name of the
|
|
219
|
+
- **Slugs** are a WordPress concept: the folder of a plugin or theme. Only the commands that produce something for WordPress use one: the `{slug}/` root folder and name of a `zip`, the `checksums` manifest, the WordPress.org slug of `deploy-svn`, and `version-check`. Each takes `--slug`, which defaults to the name of the working directory, or of the package directory with `zip --package`, and an SVN deploy target takes `slug`, which defaults to the name of the package directory. Tags and Git deploy targets don't have a slug.
|
|
147
220
|
- **`remote`** is the git remote that `changeset-tag --push` pushes tags to and that the base branch is compared on, `origin` by default.
|
|
148
221
|
|
|
149
222
|
The default strategy bumps stable `x.y.z` versions with SemVer rules, and the `x.y` and `x.y.z.w` versions common in WordPress plugins the same way: the bumped part is raised, the parts after it reset to `0`, and the version keeps its number of parts. A minor bump of `2.1` is `2.2` and a major bump is `3.0`, while a patch bump adds the third part, `2.1.1`. A patch bump of `1.2.3.4` is `1.2.4.0`, so raising only the fourth part needs a custom strategy. Configure `nextVersion( { package, bump, currentVersion, changesets } )` for other formats; changeset bumps remain `patch`, `minor` or `major`. Pre-releases are added to the next version with `--prerelease`, described below.
|
|
@@ -215,7 +288,7 @@ The `changeset-version` result lists each release with its `package`, `bump`, `c
|
|
|
215
288
|
|
|
216
289
|
After merging the release PR, `changeset-tag --since <merge-commit>^1 --push` pushes tags only for packages changed by that release. It pushes one tag at a time because GitHub doesn't trigger workflows for a push of more than three tags. wp-release doesn't create GitHub Releases: with `--json` or `--github-output`, each entry of `tags` has the `tag`, a `title` like `plugins/alpha@1.2.0`, the release `notes` and a `prerelease` flag, for a workflow step like [`softprops/action-gh-release`](https://github.com/softprops/action-gh-release) to create the release (see the example below). For initial adoption, preview all missing tags with `changeset-tag --dry-run` (without `--since`), then push if needed. To release each package with a separate workflow instead, `changeset-tag --since <merge-commit>^1 --untagged --github-output` lists the releases that don't have a tag yet, without creating any. Tag pushes need Git write credentials.
|
|
217
290
|
|
|
218
|
-
To release a single package, for example from a manual workflow, pass `--package <name>`, repeatable, and with one package `--version <version>` to fail unless the package is at that version. Packages versioned by git tags have no version file, so for them `--version` is the version to tag. Before a tag is created, every `version.to` target must match the version, so a plugin `Version` header that disagrees with its readme `Stable tag` blocks the tag. Versions with a pre-release part, like `1.0.0-rc.1`, have `prerelease` set, so their GitHub Releases can be marked as pre-releases:
|
|
291
|
+
To release a single package, for example from a manual workflow, pass `--package <name>`, repeatable, and with one package `--version <version>` to fail unless the package is at that version. Packages versioned by git tags have no version file, so for them `--version` is the version to tag. Before a tag is created, every `version.to` target must match the version, so a plugin `Version` header that disagrees with its readme `Stable tag` blocks the tag. Versions with a pre-release part, like `1.0.0-rc.1`, have `prerelease` set, so their GitHub Releases can be marked as pre-releases. This example selects one package, so `tags[0]` is its release; these fields are nested in the JSON result:
|
|
219
292
|
|
|
220
293
|
```yaml
|
|
221
294
|
- name: Tag a release
|
|
@@ -268,6 +341,7 @@ npm publish --workspace packages/library --tag pr-251
|
|
|
268
341
|
|
|
269
342
|
| Capability | CLI command | GitHub Action |
|
|
270
343
|
|---|---|---|
|
|
344
|
+
| Build a directory | `wp-release build` | |
|
|
271
345
|
| Build a ZIP | `wp-release zip` | [`wpelevator/wp-release-zip`](https://github.com/wpelevator/wp-release-zip) |
|
|
272
346
|
| Create a checksums manifest | `wp-release checksums` | [`wpelevator/wp-release-checksums`](https://github.com/wpelevator/wp-release-checksums) |
|
|
273
347
|
| Sign files | `wp-release sign` | [`wpelevator/wp-release-sign`](https://github.com/wpelevator/wp-release-sign) |
|
|
@@ -276,12 +350,15 @@ npm publish --workspace packages/library --tag pr-251
|
|
|
276
350
|
| Deploy to WordPress.org SVN | `wp-release deploy-svn` | [`wpelevator/wp-release-deploy-svn`](https://github.com/wpelevator/wp-release-deploy-svn) |
|
|
277
351
|
| Deploy to a Git repository | `wp-release deploy-git` | |
|
|
278
352
|
|
|
279
|
-
|
|
353
|
+
The names below are scoped to their commands. `--output` selects the primary artifact of each command; ZIP packaging uses `--checksums-file` for its accompanying manifest:
|
|
280
354
|
|
|
281
355
|
| Concept | CLI | GitHub Action | Library | Meaning |
|
|
282
356
|
|---|---|---|---|---|
|
|
283
357
|
| Plugin or theme directory | `<source-dir>` argument or `--source-dir` | `source-dir` | `sourceDir` | Directory that a command reads, whether it's the repository or a build directory. |
|
|
284
|
-
|
|
|
358
|
+
| Build directory | `build --output <dir>` | — | `outputDir` | Directory written by `build`; files go directly inside it. |
|
|
359
|
+
| ZIP file | `zip --output <file>` | `output` in ZIP action | `zipPath` | Destination passed to `zipPackage()`; also returned as `zipPath`. |
|
|
360
|
+
| Standalone checksums file | `checksums --output <file>` | `output` in checksums action | first argument of `writeChecksums()` | Manifest destination relative to the working directory; the CLI returns its absolute path as `checksumsPath`. |
|
|
361
|
+
| Checksums filename when zipping | `zip --checksums-file <file>` | `checksums-file` in ZIP action | `checksumsFile` | Relative manifest filename beside the ZIP or inside its package root when embedded. |
|
|
285
362
|
| ZIP to deploy | `--from-zip <file>` | `from-zip` | `fromZip` | Existing ZIP read as the deploy source. |
|
|
286
363
|
| Git tag to compare | `--git-tag <tag>` | | `gitTag` | Git tag checked by `version-check`. |
|
|
287
364
|
| SVN tag to create | `--svn-tag <version>` | `svn-tag` | `svnTag` | Tag written by `deploy-svn`, distinct from a git tag. |
|
|
@@ -291,13 +368,20 @@ Each concept has one name, written in the syntax of each interface:
|
|
|
291
368
|
| Source history | `--split-prefix <dir>` | | `splitPrefix` | Directory of a monorepo package whose history, from `git subtree split`, becomes the source history of `deploy-git` releases. |
|
|
292
369
|
| SVN credentials | `--svn-username <name>`, `SVN_USERNAME` and `SVN_PASSWORD` env vars | `svn-username`, `svn-password` | `svnUsername`, `svnPassword` | Needed only to commit. There's no password flag, since it would show in the process list; the action inputs and library options fall back to the env vars. |
|
|
293
370
|
| Log format | `--log-format <text\|github>`, `--ci` for `github` | always `github` | `writer: new TextWriter()` or `new GitHubActionsWriter()` | How the log is printed: plain text, or GitHub Actions annotations and job summaries. Each name matches the start of its writer class. |
|
|
371
|
+
| Release config | `--config <path>` | `config` in ZIP, checksums and SVN actions | `configFile` | Explicit JavaScript config path for package selection, integrity settings or deployment. |
|
|
372
|
+
| Release tag format | `--tag-format <format>` | `tag-format` in SVN action | `tagFormat` | How `release-ref` identifies a package and its version. |
|
|
373
|
+
| Release ref to deploy | `--release-ref <ref>` | `release-ref` in SVN action | `ref` | Read configured packages and deployment targets from a release ref. |
|
|
294
374
|
| Assets sync | `--no-assets` to skip | `assets: false` to skip | `assets` | Booleans are positive in the library and the action; `--no-assets` is only the CLI negation, never `noAssets` or a `no-assets` input. |
|
|
295
375
|
|
|
296
|
-
CLI JSON results use camelCase keys like `zipPath`, and GitHub Action outputs use kebab-case like `zip-path`.
|
|
376
|
+
CLI JSON results use camelCase keys like `zipPath`, and GitHub Action outputs use kebab-case like `zip-path`. The ZIP action supports `package` and `config`, the checksums action supports `config` for codec selection, and the SVN action supports `release-ref`, repeated `package` values as newline-separated input, `list`, `config` and `tag-format`.
|
|
377
|
+
|
|
378
|
+
The ZIP CLI result uses `checksumsEmbedded` for the package-relative embedded manifest path. Its `signatures` array includes external absolute paths and embedded package-relative paths, while `checksumsSignatures` contains only manifest signatures. The ZIP action exposes those locations as `checksums-embedded`, `signatures` for external signatures, and `signatures-embedded` for embedded signatures, so asset uploads receive local files. With CLI `--github-output`, signature arrays remain in the JSON `result` output.
|
|
379
|
+
|
|
380
|
+
Library classes are named after the capability (`SvnDeploy`, `Svn`, `GitDeploy`) without repeating "release".
|
|
297
381
|
|
|
298
382
|
### File selection
|
|
299
383
|
|
|
300
|
-
`zip`, `checksums`, `deploy-svn` and `deploy-git` select files the same way:
|
|
384
|
+
`build`, `zip`, `checksums`, `deploy-svn` and `deploy-git` select files the same way:
|
|
301
385
|
|
|
302
386
|
| Option | Description |
|
|
303
387
|
|---|---|
|
|
@@ -314,6 +398,25 @@ The commands read the same headers as WordPress. The main plugin file is the top
|
|
|
314
398
|
|
|
315
399
|
`--version` defaults to the `Version` header in `zip`, `checksums` and `version-check`, and `checksums --type` defaults to the detected package type.
|
|
316
400
|
|
|
401
|
+
### Build a directory
|
|
402
|
+
|
|
403
|
+
`build` and `zip` use the same internal helper to load file rules, exclude their outputs, select release files and reject an empty selection. `build` copies the selected files directly into `--output <dir>`, without a slug wrapper or ZIP. Checksums and manifest signatures are optional. It supports the shared file selection flags above and `--package <name>` with `--config <path>` for configured package rules. A configured package cannot be combined with a source directory or file selection flags.
|
|
404
|
+
|
|
405
|
+
```sh
|
|
406
|
+
wp-release build --output dist/code-prettify
|
|
407
|
+
wp-release build --package plugins/alpha --output dist/alpha
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The destination is required and automatically excluded when it is inside the source. It cannot be the source directory or an ancestor, including through symlinked parents. Existing destinations require `--force`, which replaces the directory and removes stale files; existing files and symbolic links are rejected. Files are copied to a temporary directory before the existing build is replaced. Selected symbolic links are rejected as with ZIP packaging. `--json` returns `outputDir`, `files` and, for configured builds, `package`; it also returns `checksumsPath` when a manifest is generated, and `signatures` and `publicKey` when signing is requested. `--github-output` exposes these results too.
|
|
411
|
+
|
|
412
|
+
`--checksums` writes `<output-dir>.checksums.json` beside the directory and hashes the copied build files. `--sign-checksums` generates and signs that manifest, implying `--checksums`. Use `--slug` and `--version` to override its metadata. The manifest and its signatures are excluded from the source selection, so rebuilding with `--force` does not package previous sidecars. Existing manifests and requested signature files also require `--force`; signing keys, formats and signature destinations are validated before creating or replacing the build. `--sign` is unsupported because a directory has no single file to sign.
|
|
413
|
+
|
|
414
|
+
```sh
|
|
415
|
+
wp-release build --output dist/code-prettify --sign-checksums
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
In the Deploy workflow, a directory build can be deployed with `wp-release deploy-svn dist/code-prettify --slug code-prettify --trunk --svn-tag "$GITHUB_REF_NAME" --no-distignore`. Keep `--from-zip` when the SVN deployment must match the attached ZIP exactly. Run dependency installation and compilation before `build`; it only selects and copies files.
|
|
419
|
+
|
|
317
420
|
### ZIP
|
|
318
421
|
|
|
319
422
|
The [`wp-release-zip`](https://github.com/wpelevator/wp-release-zip) GitHub Action runs this command.
|
|
@@ -326,11 +429,37 @@ wp-release zip dist --slug my-plugin --checksums
|
|
|
326
429
|
|
|
327
430
|
- `--root-folder <name>` changes the root folder and `--no-root-folder` puts the files at the root of the ZIP.
|
|
328
431
|
- `--filename-format` changes the name, like `--filename-format {slug}` for `my-plugin.zip`.
|
|
329
|
-
- Entries are sorted and use a fixed timestamp and file mode,
|
|
432
|
+
- Entries are sorted and use a fixed timestamp and file mode. Built-in metadata is deterministic except for embedded minisign signatures, which include their signing timestamp; custom codec callbacks control their own encoded bytes.
|
|
330
433
|
- `--checksums` also writes the [checksums manifest](#checksums) of the archived files next to the ZIP, with `.checksums.json` instead of `.zip`.
|
|
434
|
+
- `--sign` signs only the ZIP with `WP_RELEASE_SIGNING_KEY`. `--sign-checksums` generates and signs only the checksums manifest, implying `--checksums`. Use both flags to sign both files. External signatures are written alongside their files; embedded manifest signatures are written inside the package. The default format is WordPress `.sig`, unless integrity configuration selects another format.
|
|
435
|
+
- `--sign-format` selects formats for requested signatures, such as `--sign-format wordpress,minisign,ssh`. It requires `--sign` or `--sign-checksums` and never enables signing by itself. Signing keys, formats and existing signature destinations are validated before writing the ZIP or manifest. `--force` also overwrites requested signatures. Job summaries include signatures and public keys; JSON results include `signatures` and `publicKey`.
|
|
331
436
|
- An existing ZIP or manifest isn't overwritten without `--force`. A ZIP or manifest written inside the source directory is never archived.
|
|
332
437
|
- `--package <name>` zips a package of the [configured packages](#deploy-configured-packages), by identifier or path under the configured root like `plugins/alpha`, instead of a source directory: its directory with the `files` config of its package group, the same files that its deploys select. The slug defaults to the directory name and the version to the `Version` header, and `--slug` and `--version` still set them. Any package of the config works, with or without a version or deploy targets. To zip what a deploy just released, pass the `package` and `version` of the `deploy-git --release-ref` result, in the same job and from the same build, so the ZIP has exactly the files of the release tag in the release repository. It can't be combined with `<source-dir>` or the file selection options. The `--json` result has the `package`, `slug` and `version`. `zipPackage()` builds the same ZIP from a script, with `getPackageFiles()` for the directory and the files of a package.
|
|
333
438
|
|
|
439
|
+
To embed a signed manifest with a stable filename, use:
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
wp-release zip dist --checksums-file checksums.json --embed-checksums --sign-checksums
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
See [Checksums](#checksums) for filenames, placement, formats and configuration.
|
|
446
|
+
|
|
447
|
+
### Signing targets for builds and ZIPs
|
|
448
|
+
|
|
449
|
+
| Flag | `zip` | `build` |
|
|
450
|
+
|---|---|---|
|
|
451
|
+
| `--checksums` | Generate an unsigned manifest beside the ZIP. | Generate an unsigned manifest beside the directory. |
|
|
452
|
+
| `--sign` | Sign only the ZIP. | Unsupported. |
|
|
453
|
+
| `--sign-checksums` | Generate and sign the manifest. | Generate and sign the manifest. |
|
|
454
|
+
| `--sign-format` | Choose formats for explicitly requested signatures. | Same; requires `--sign-checksums`. |
|
|
455
|
+
|
|
456
|
+
```sh
|
|
457
|
+
wp-release zip --slug code-prettify --sign --sign-checksums
|
|
458
|
+
wp-release build --output dist/code-prettify --sign-checksums --sign-format minisign
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
These flags deliberately replace the former implicit signing behavior: `zip --checksums --sign` now leaves the manifest unsigned, and `--sign-format` alone is rejected. Add `--sign-checksums` to sign a manifest and `--sign` to sign a ZIP. The standalone `checksums` and `sign` commands remain available for workflows that write and sign a manifest inside the source before packaging it.
|
|
462
|
+
|
|
334
463
|
### Checksums
|
|
335
464
|
|
|
336
465
|
The [`wp-release-checksums`](https://github.com/wpelevator/wp-release-checksums) GitHub Action runs this command.
|
|
@@ -359,26 +488,92 @@ wp-release checksums dist --version 1.2.0
|
|
|
359
488
|
|
|
360
489
|
Because the manifest lists the files and not the ZIP, it stays valid for any copy of the same files: the GitHub release ZIP, the ZIP that WordPress.org builds from SVN, or an installed plugin on a site.
|
|
361
490
|
|
|
362
|
-
|
|
491
|
+
#### Filenames, embedding and signing
|
|
363
492
|
|
|
364
|
-
|
|
493
|
+
`--checksums-file <file>` enables checksums and sets a relative filename independently of the ZIP name. Without `--embed-checksums`, it resolves against the ZIP output directory; with embedding, it resolves against the package root inside the ZIP (the archive root with `--no-root-folder`). Nested paths are supported, but absolute paths, backslashes, drive letters, control characters and dot segments are rejected. Embedding emits no external manifest and never modifies the source directory. Embedded minisign signatures contain a signing timestamp, so ZIP bytes can differ across signing times; unsigned metadata and WordPress/SSH signatures remain deterministic. Selected source files must not collide with generated manifest or signature entries.
|
|
365
494
|
|
|
366
495
|
```bash
|
|
367
|
-
|
|
496
|
+
# External manifest beside releases/my-plugin.1.5.1.zip.
|
|
497
|
+
wp-release zip dist --output releases/my-plugin.1.5.1.zip --checksums-file checksums.json --sign-checksums
|
|
498
|
+
|
|
499
|
+
# Signed manifest inside the package, with a stable filename.
|
|
500
|
+
wp-release zip dist --output releases/my-plugin.1.5.1.zip --checksums-file checksums.json --embed-checksums --sign-checksums
|
|
501
|
+
|
|
502
|
+
# Sign the completed downloadable archive separately.
|
|
503
|
+
wp-release sign releases/my-plugin.1.5.1.zip
|
|
504
|
+
|
|
505
|
+
# Verify installed files or an archive using its embedded manifest.
|
|
506
|
+
wp-release verify installed-plugin --public-key "$PUBLIC_KEY"
|
|
507
|
+
wp-release verify --zip releases/my-plugin.1.5.1.zip --public-key "$PUBLIC_KEY"
|
|
368
508
|
```
|
|
369
509
|
|
|
370
|
-
-
|
|
371
|
-
- `verify` doesn't read `.distignore`, so that an ignore file inside the checked directory can't hide added files. `--exclude <patterns>` skips paths in both the directory and the manifest, for files that aren't part of the release, such as caches.
|
|
372
|
-
- Manifest paths outside the package folder (absolute or with `..`) are rejected.
|
|
373
|
-
- `--zip <file|url>` checks the files of a ZIP instead of a directory. The ZIP can be a URL, such as the one that WordPress.org builds for a release, and its single root folder is found automatically (or set `--root-folder`). The ZIP is extracted to a temporary directory after the same safety checks as `deploy-svn --from-zip`.
|
|
374
|
-
- The version in the manifest must match the `Version` header of the checked files, so that an older release with its own valid manifest can't pass as a newer one.
|
|
375
|
-
- A manifest can't list itself. A copy of the manifest and of its signature inside the checked files, at the root with the same names as the `--checksums` file or at the path of `--embedded`, is skipped when comparing files, but it must be identical to the `--checksums` file and its signature. The signature is the `.sig` file, or the file of `--signature-format`. A path that the manifest lists is never skipped, and a file that isn't a matching copy is reported as not in the checksums. See [Manifest inside the plugin](#manifest-inside-the-plugin).
|
|
510
|
+
`--sign-checksums` enables checksums and signs only the manifest, before embedding when requested. It reads `WP_RELEASE_SIGNING_KEY` and uses `--sign-format wordpress,minisign,ssh` to select formats; each signature travels with the manifest. `--sign-format` selects formats for explicitly requested signing targets and never enables signing. `--sign` signs only the final ZIP; `--sign-checksums` controls the manifest in either placement mode. Use both flags to sign both targets. `--force` permits replacing external output files and signatures. `--json` reports external manifests in `checksumsPath`, embedded package-relative paths in `checksumsEmbedded`, and manifest signatures in `checksumsSignatures`.
|
|
376
511
|
|
|
377
|
-
|
|
512
|
+
The default WordPress filename remains the ZIP basename with `.checksums.json` instead of `.zip`, in either placement mode. Choose `--checksums-file checksums.json` for a stable name. The version remains in the JSON content.
|
|
513
|
+
|
|
514
|
+
#### Formats
|
|
515
|
+
|
|
516
|
+
`--checksums-format` chooses the manifest format. `wordpress-json` (default) uses the WordPress-compatible structure shown above. `sha256sum` uses GNU-compatible SHA-256 lines and escaping, with `SHA256SUMS` as its default filename. It carries no package identity or version, and standard `sha256sum --check` checks listed files without rejecting additional files; `wp-release verify` also checks for extra files.
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
wp-release zip dist --embed-checksums --checksums-format sha256sum --sign-checksums --sign-format minisign
|
|
520
|
+
wp-release verify --zip my-plugin.1.5.1.zip --checksums-format sha256sum --public-key "$MINISIGN_PUBLIC_KEY" --signature-format minisign
|
|
521
|
+
# From the extracted package directory:
|
|
522
|
+
minisign -Vm SHA256SUMS -P "$MINISIGN_PUBLIC_KEY"
|
|
523
|
+
sha256sum --check SHA256SUMS
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
`--checksums-algorithms md5,sha256` selects JSON digest fields; lists replace configured lists. Supported algorithms are MD5 (for compatibility) and SHA-256. The `sha256sum` codec requires SHA-256. The standalone `checksums` command also accepts `--checksums-format`, keeping `--output` relative to the working directory.
|
|
527
|
+
|
|
528
|
+
#### Configuration and custom codecs
|
|
529
|
+
|
|
530
|
+
The optional top-level `integrity` configuration maps to these flags. Explicit CLI values override configuration; `--no-embed-checksums` and `--no-sign-checksums` disable their configured behaviors. Existing release configurations retain their `packages` array; use `packages: []` for a configuration containing only integrity settings.
|
|
531
|
+
|
|
532
|
+
```js
|
|
533
|
+
export default {
|
|
534
|
+
packages: [],
|
|
535
|
+
integrity: {
|
|
536
|
+
codec: 'wordpress-json', // --checksums-format
|
|
537
|
+
algorithms: ['md5', 'sha256'], // --checksums-algorithms
|
|
538
|
+
file: 'checksums.json', // --checksums-file
|
|
539
|
+
embed: true, // --embed-checksums
|
|
540
|
+
sign: { formats: ['wordpress'] }, // --sign-checksums and --sign-format
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
```
|
|
378
544
|
|
|
379
|
-
|
|
545
|
+
Use `codec: 'sha256sum'`, `algorithms: ['sha256']`, `file: 'SHA256SUMS'` and `sign: { formats: ['minisign'] }` for the GNU setup. Custom codecs provide `id`, a default `filename`, and `encode`/`decode` callbacks, which may be asynchronous. Generation validates the round trip before writing artifacts: codecs must preserve all selected file paths, represented digests and any represented package identity. They receive and return the existing WordPress-compatible `ChecksumsManifest` model (`plugin` or `theme`, optional `version`, and a `files` map of relative paths to digest fields), keeping format customization separate from selection, hashing and signing.
|
|
380
546
|
|
|
381
|
-
|
|
547
|
+
```js
|
|
548
|
+
export default {
|
|
549
|
+
packages: [],
|
|
550
|
+
integrity: {
|
|
551
|
+
file: 'metadata/integrity.json',
|
|
552
|
+
embed: true,
|
|
553
|
+
sign: { formats: ['wordpress'] },
|
|
554
|
+
codec: {
|
|
555
|
+
id: 'custom-v1',
|
|
556
|
+
filename: 'integrity.json',
|
|
557
|
+
encode(manifest) {
|
|
558
|
+
return Buffer.from(JSON.stringify({ schema: 'custom-v1', payload: manifest }) + '\n');
|
|
559
|
+
},
|
|
560
|
+
decode(bytes) {
|
|
561
|
+
const document = JSON.parse(Buffer.from(bytes).toString('utf8'));
|
|
562
|
+
if (document.schema !== 'custom-v1') {
|
|
563
|
+
throw new Error('Unknown integrity schema.');
|
|
564
|
+
}
|
|
565
|
+
return document.payload;
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
};
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
`zip` and `checksums` read the caller's release configuration. For verification of custom codecs or configured filenames, pass `--config wp-release.config.mjs` explicitly. See [Verify](#verify).
|
|
573
|
+
|
|
574
|
+
#### Manifest inside the plugin
|
|
575
|
+
|
|
576
|
+
For a manifest that is also published as a separate release asset or deployed directly from a source directory, write and sign it in that directory before packaging. Use `zip --embed-checksums` as described [above](#filenames-embedding-and-signing) when only the archive should contain the generated metadata. The source-directory workflow uses the same manifest for installed copies and release assets:
|
|
382
577
|
|
|
383
578
|
```bash
|
|
384
579
|
wp-release checksums build --slug my-plugin --output build/checksums.json
|
|
@@ -387,7 +582,7 @@ wp-release zip build --slug my-plugin
|
|
|
387
582
|
wp-release deploy-svn build --trunk --svn-tag 1.2.0
|
|
388
583
|
```
|
|
389
584
|
|
|
390
|
-
- Write the manifest into the directory that is zipped and deployed.
|
|
585
|
+
- Write the manifest into the directory that is zipped and deployed. The `checksums` command leaves its output manifest and its signatures (`<name>.sig`, `<name>.minisig` and `<name>.sshsig`) out of the hashes, so running it again with the same output path and `--force` gives the same manifest. `verify` checks matching copies as described below. Sign it last, since the signature covers the exact bytes of the manifest.
|
|
391
586
|
- `zip` and `deploy-svn` carry the pair like any other file. Don't use `zip --checksums` here, since it would write a second manifest that lists the first one. Attach `build/checksums.json` and `build/checksums.json.sig` to the GitHub release as they are.
|
|
392
587
|
- The signature covers file hashes and not a ZIP, so it stays valid for the ZIP that WordPress.org builds from SVN. Check what WordPress.org serves after a deploy, with the manifest and public key from your build:
|
|
393
588
|
|
|
@@ -400,6 +595,25 @@ wp-release deploy-svn build --trunk --svn-tag 1.2.0
|
|
|
400
595
|
|
|
401
596
|
The public key has to come from somewhere other than the plugin that it checks, because someone who can overwrite the plugin files can also replace a key and a signature inside them. A copy of the manifest also doesn't prevent a downgrade to an older release that was signed too, so the installing side has to enforce a minimum version.
|
|
402
597
|
|
|
598
|
+
### Verify
|
|
599
|
+
|
|
600
|
+
`verify` checks a directory against a manifest from `checksums` or from the WordPress.org checksums API, and reports every changed, missing and extra file. It exits with `2` unless all files match:
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
wp-release verify wp-content/plugins/my-plugin --checksums my-plugin.1.2.0.checksums.json
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
- A file matches when its SHA-256 equals the manifest. Manifests without SHA-256 fall back to MD5, and a hash can also be a list of accepted values.
|
|
607
|
+
- `verify` doesn't read `.distignore`, so that an ignore file inside the checked directory can't hide added files. `--exclude <patterns>` skips paths in both the directory and the manifest, for files that aren't part of the release, such as caches.
|
|
608
|
+
- Manifest paths outside the package folder (absolute or with `..`) are rejected.
|
|
609
|
+
- `--zip <file|url>` checks the files of a ZIP instead of a directory. The ZIP can be a URL, such as the one that WordPress.org builds for a release, and its single root folder is found automatically (or set `--root-folder`). The ZIP is extracted to a temporary directory after the same safety checks as `deploy-svn --from-zip`.
|
|
610
|
+
- The version in the manifest must match the `Version` header of the checked files, so that an older release with its own valid manifest can't pass as a newer one.
|
|
611
|
+
- A manifest can't list itself. A copy of the manifest and of its signature inside the checked files, at the root with the same names as the `--checksums` file or at the path of `--embedded`, is skipped when comparing files, but it must be identical to the `--checksums` file and its signature. The signature is the `.sig` file, or the file of `--signature-format`. A path that the manifest lists is never skipped, and a file that isn't a matching copy is reported as not in the checksums. See [Manifest inside the plugin](#manifest-inside-the-plugin).
|
|
612
|
+
|
|
613
|
+
A checksums manifest proves only that the files match the manifest. With `--public-key <key>`, `verify` first checks `<checksums>.sig` against the Ed25519 public key and fails with exit code `2` before comparing any file when the signature is missing or invalid, so that the manifest itself can be trusted. `--signature-format minisign` or `ssh` checks `<checksums>.minisig` or `<checksums>.sshsig` instead. The key can be the base64 key, the minisign public key or the `ssh-ed25519` public key, since they're forms of the same key. See [Sign](#sign).
|
|
614
|
+
|
|
615
|
+
Verification never executes configuration from inside a downloaded package. Without `--checksums`, `verify` reads the embedded filename selected by `--checksums-file`, explicit configuration, or the codec default. For a versioned embedded filename, pass that filename explicitly. With a public key, signatures are checked before decoding; the selected signature format must exist (WordPress `.sig` by default; pass `--signature-format minisign` or `ssh` for those formats), and all other present supported signature formats are also authenticated when verifying the embedded manifest. Without a public key, checksums detect changes but do not authenticate the publisher. Trusted public keys must come from outside the package.
|
|
616
|
+
|
|
403
617
|
### Sign
|
|
404
618
|
|
|
405
619
|
The [`wp-release-sign`](https://github.com/wpelevator/wp-release-sign) GitHub Action runs this command.
|
|
@@ -414,6 +628,7 @@ WP_RELEASE_SIGNING_KEY=… wp-release sign my-plugin.1.2.0.zip my-plugin.1.2.0.c
|
|
|
414
628
|
- The key is read from the `WP_RELEASE_SIGNING_KEY` environment variable only, as a base64 32-byte Ed25519 seed or a 64-byte libsodium secret key, the formats that `sodium_crypto_sign_seed_keypair()` and the update-pilot-server package signing key take. There's no flag for it, since it would show in the process list, and it's never logged.
|
|
415
629
|
- `--verbose` prints the public key of the signing key in the form of each written format. `--json` returns it as `publicKey`, and in every form as `publicKeys`. Pass it to `verify --public-key` and to the plugin that checks the signatures.
|
|
416
630
|
- An existing signature file isn't overwritten without `--force`.
|
|
631
|
+
- `zip --sign` signs only the ZIP; add `--sign-checksums` to sign its manifest too. `signFiles()` signs files from a script.
|
|
417
632
|
|
|
418
633
|
#### Signature formats
|
|
419
634
|
|
|
@@ -433,7 +648,7 @@ wp-release sign my-plugin.1.2.0.zip --format wordpress,minisign,ssh
|
|
|
433
648
|
- `ssh` is the armored `SSHSIG` format that `ssh-keygen -Y sign` writes, with a SHA-512 digest and the `file` namespace, so that the signature can't pass as one for commits or another namespace. It's written to `.sshsig` instead of the `.sig` of `ssh-keygen`, which the `wordpress` format uses. Put the SSH public key in an `allowed_signers` file as `<identity> ssh-ed25519 AAAA…`.
|
|
434
649
|
- All formats are Ed25519 signatures by the same key, so one key and one secret cover all of them. The tests check each format with `minisign`, `ssh-keygen` and libsodium, and check the signatures that `minisign` and `ssh-keygen` write with `wp-release verify`.
|
|
435
650
|
|
|
436
|
-
Generate a key pair with `wp-release keygen`. It prints the private seed once as `WP_RELEASE_SIGNING_KEY=…` and the matching public key, also as a minisign public key and an `ssh-ed25519` public key.
|
|
651
|
+
Generate a key pair with `wp-release keygen`. It prints the private seed once as `WP_RELEASE_SIGNING_KEY=…` and the matching public key, also as a minisign public key and an `ssh-ed25519` public key. Generate keys locally and store the private seed in your secret manager or CI secrets. Distribute the public key to verifiers. `keygen --github-output` is rejected to keep the private seed out of workflow outputs; regular and `--json` output both include the seed.
|
|
437
652
|
|
|
438
653
|
### Version check
|
|
439
654
|
|
|
@@ -499,7 +714,7 @@ Validation:
|
|
|
499
714
|
- `--readme-only` can't be combined with `--svn-tag`, since a tag is a release snapshot that a readme update never changes.
|
|
500
715
|
- `--version` defaults to `--svn-tag`, then the `Version` header of the deployed files; when `--version` and `--svn-tag` are both set they must match.
|
|
501
716
|
- Pre-release versions like `1.0.0-rc.1` can be tagged, but syncing one to `trunk/` warns, since trunk is for the development version.
|
|
502
|
-
- `--trunk` without `--svn-tag` works but warns,
|
|
717
|
+
- `--trunk` without `--svn-tag` works but warns, to remind you that updating trunk alone doesn't create a version tag for stable downloads.
|
|
503
718
|
- A version mismatch throws a `VersionMismatchError`, a `ValidationError` with the `mismatches` and a problem for each, with its file and line. See [Logging](#logging).
|
|
504
719
|
|
|
505
720
|
### Deploy to Git
|
|
@@ -640,7 +855,7 @@ const { releases } = await release.version( { dryRun: true } );
|
|
|
640
855
|
const { tags } = await release.tag( { dryRun: true } );
|
|
641
856
|
```
|
|
642
857
|
|
|
643
|
-
`ChangesetRelease` runs the `changeset-*` commands: `add()`, `getStatus()`, `version()` and `tag()`, plus `getPlan()` and `getPackages()`. Each call reads the configuration, packages and pending changesets again, so one instance can version and then tag. It takes a git runner from `createGitRunner()` as `git`, so that tests can replace it. The functions `addChangeset()`, `getChangesetStatus()`, `versionChangesets()`, `tagChangesets()`, `getChangesetReleasePlan()` and `getChangesetPackages()` do the same with the default runners, in one call.
|
|
858
|
+
`ChangesetRelease` runs the `changeset-*` commands: `add()`, `getStatus()`, `version()` and `tag()`, plus `getPlan()` and `getPackages()`. Each call reads the configuration, packages and pending changesets again, so one instance can version and then tag. It takes a git runner from `createGitRunner()` as `git`, so that tests can replace it. Without a config file, it detects a single root project. Its optional `singleProject` settings accept `versions` (the existing version descriptors), `versionFrom` (the same selector as `--version-from`), `baseBranch`, `remote`, `changesets`, `readmeChangelog` and `changelogInsertAfter`; `tagFormat` remains a repository option. The functions `addChangeset()`, `getChangesetStatus()`, `versionChangesets()`, `tagChangesets()`, `getChangesetReleasePlan()` and `getChangesetPackages()` do the same with the default runners, in one call.
|
|
644
859
|
|
|
645
860
|
### Logging
|
|
646
861
|
|
|
@@ -745,16 +960,20 @@ npm run lint --workspace @wpelevator/wp-release
|
|
|
745
960
|
- `test` runs the `.ts` tests directly with `node --test`, using Node.js type stripping, so it needs Node.js 22.18 or later but no build. The SVN integration tests in `svn-deploy.integration.test.ts` create repositories with `svnadmin` and deploy to their `file://` URLs; they're skipped when `svn` or `svnadmin` isn't installed.
|
|
746
961
|
- `lint` runs ESLint and a `tsc` type check.
|
|
747
962
|
|
|
748
|
-
The `wpelevator-source` export condition points at `src/index.ts`. The tests and
|
|
963
|
+
The `wpelevator-source` export condition points at `src/index.ts`. The wp-release tests and monorepo CLI scripts use it to work with the source instead of a `dist/` build. Action builds use the declared dependency's default export, which resolves to `dist/` for the workspace copy and the published package. Build wp-release before building the actions.
|
|
964
|
+
|
|
965
|
+
`execute( 'deploy-svn', args, { cwd, logger, svnRunner } )` runs a single-plugin deployment or a configured plan or deployment. The optional `svnRunner` replaces the external command runner in both modes; without it, the command uses its normal runner.
|
|
749
966
|
|
|
750
967
|
### Architecture
|
|
751
968
|
|
|
752
969
|
wp-release has two layers, so that it fits any repository layout:
|
|
753
970
|
|
|
754
|
-
- **Primitives** work on the directory, file or values they're given and know nothing about the repository: `zip <dir>`, `checksums`, `sign`, `verify`, `version-check`, `deploy-git <dir>` and `deploy-svn <dir>`.
|
|
755
|
-
- **The config layer** reads `wp-release.config.js` for everything about the repository: where packages are (`root` and the package globs), how their versions and tags are named (`version`, `tagFormat`), which files they ship (`files`), where they deploy (`deploy`) and the git `remote`. The changeset commands, `deploy-git` and `deploy-svn --release-ref`, and `zip --package` belong to it, and the global `--config` and `--tag-format` options select or adjust the config. Only `--release-ref` reads which package and version a ref releases. A primitive with a config mode, like `deploy-git --release-ref` beside `deploy-git <dir>` or `zip --package` beside `zip <dir>`, only takes the package directory, its files and its targets from the config there, and otherwise works the same.
|
|
971
|
+
- **Primitives** work on the directory, file or values they're given and know nothing about the repository: `build <dir>`, `zip <dir>`, `checksums`, `sign`, `verify`, `version-check`, `deploy-git <dir>` and `deploy-svn <dir>`.
|
|
972
|
+
- **The config layer** reads `wp-release.config.js` for everything about the repository: where packages are (`root` and the package globs), how their versions and tags are named (`version`, `tagFormat`), which files they ship (`files`), where they deploy (`deploy`) and the git `remote`. The changeset commands (with a single-project fallback when no config exists), `deploy-git` and `deploy-svn --release-ref`, and `build --package` and `zip --package` belong to it, and the global `--config` and `--tag-format` options select or adjust the config. `zip` and `checksums` also read caller configuration for integrity settings even without `--package`; verification reads it only when explicitly selected. Only `--release-ref` reads which package and version a ref releases. A primitive with a config mode, like `deploy-git --release-ref` beside `deploy-git <dir>` or `zip --package` beside `zip <dir>`, only takes the package directory, its files and its targets from the config there, and otherwise works the same.
|
|
973
|
+
|
|
974
|
+
Configured repositories use the layout and naming conventions their config sets. Config-free changeset releases use the root-only detection and defaults described above.
|
|
756
975
|
|
|
757
|
-
|
|
976
|
+
The GitHub Actions are adapters to these commands: they use the same flag names and `execute()` entry point, with secrets supplied through environment variables. The SVN action injects a command runner that checks or installs SVN only when a deployment runs its first SVN command; config planning and deployment behavior remain in `wp-release`.
|
|
758
977
|
|
|
759
978
|
The package uses ES modules as its unit of encapsulation, like most Node.js CLI tools: a module exports a few functions and keeps its helpers private, so there's no class to wrap them in. A class is used only when it holds state across steps or binds dependencies, so that callers and tests can replace them:
|
|
760
979
|
|
|
@@ -769,6 +988,7 @@ The changeset release modules, from the entry point down:
|
|
|
769
988
|
| Module | Responsibility |
|
|
770
989
|
|---|---|
|
|
771
990
|
| `changeset-release.ts` | `ChangesetRelease`, which plans releases and runs `add()`, `getStatus()`, `version()` and `tag()`, and the function form of each operation. |
|
|
991
|
+
| `single-project.ts` | Resolving the config-free root project, its version source and targets, and shared release settings. |
|
|
772
992
|
| `changeset-config.ts` | Loading and validating `wp-release.config.js`, and the version source and target helpers. |
|
|
773
993
|
| `changeset-packages.ts` | Discovering the packages of the configured globs, with or without a version, and finding the changed ones. |
|
|
774
994
|
| `changeset-files.ts` | Reading, writing and consuming changeset files. |
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IntegrityConfig } from './integrity.ts';
|
|
1
2
|
import type { Changeset, ChangesetBump, ChangesetPackage } from './changesets.ts';
|
|
2
3
|
export type PackageVersionSource = {
|
|
3
4
|
kind: 'package-json';
|
|
@@ -217,6 +218,8 @@ export interface DependentsConfig {
|
|
|
217
218
|
}
|
|
218
219
|
/** JavaScript configuration for changeset-aware releases. */
|
|
219
220
|
export interface ChangesetConfig {
|
|
221
|
+
/** Default checksum format, placement and manifest signing for ZIPs. */
|
|
222
|
+
integrity?: IntegrityConfig;
|
|
220
223
|
/** Branch receiving ordinary changes. */
|
|
221
224
|
baseBranch?: string;
|
|
222
225
|
/** Git remote that tags are pushed to and the base branch is compared on, origin by default. */
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import type { SingleProjectOptions } from './single-project.ts';
|
|
1
2
|
import type { Changeset, ChangesetBump, ChangesetPackage, ChangesetReleasePlan, PackageRelease, VersionedRelease } from './changesets.ts';
|
|
2
3
|
import type { GitRunner } from './git.ts';
|
|
3
4
|
import { ReleaseRepository } from './release-repository.ts';
|
|
4
5
|
export interface ChangesetRepository {
|
|
5
6
|
cwd: string;
|
|
6
7
|
configFile?: string;
|
|
8
|
+
/** Single-project fallback settings, used only without a release config. */
|
|
9
|
+
singleProject?: SingleProjectOptions;
|
|
7
10
|
/** Tag format that replaces the tagFormat of the config. */
|
|
8
11
|
tagFormat?: string;
|
|
9
12
|
}
|
|
@@ -99,6 +102,7 @@ export interface ChangesetStatusResult {
|
|
|
99
102
|
export declare class ChangesetRelease {
|
|
100
103
|
readonly cwd: string;
|
|
101
104
|
readonly configFile: string | undefined;
|
|
105
|
+
readonly singleProject: SingleProjectOptions | undefined;
|
|
102
106
|
readonly tagFormat: string | undefined;
|
|
103
107
|
readonly repository: ReleaseRepository;
|
|
104
108
|
/**
|
|
@@ -109,7 +113,7 @@ export declare class ChangesetRelease {
|
|
|
109
113
|
* Discover configured packages, pending changesets, and their proposed
|
|
110
114
|
* versions.
|
|
111
115
|
*
|
|
112
|
-
* @return A release plan; `undefined`
|
|
116
|
+
* @return A release plan; `undefined` without a config or a recognized root project.
|
|
113
117
|
*/
|
|
114
118
|
getPlan(): Promise<ChangesetReleasePlan | undefined>;
|
|
115
119
|
/**
|
|
@@ -200,7 +204,7 @@ export declare class ChangesetRelease {
|
|
|
200
204
|
* Load the configuration, discover the packages and plan the releases of
|
|
201
205
|
* the pending changesets, including the releases of their dependents.
|
|
202
206
|
*
|
|
203
|
-
* @return Release context, undefined without a config.
|
|
207
|
+
* @return Release context, undefined without a config or a recognized root project.
|
|
204
208
|
* @throws {ValidationError} For an invalid config, plan or changeset.
|
|
205
209
|
*/
|
|
206
210
|
private loadContext;
|
|
@@ -218,6 +222,8 @@ export declare class ChangesetRelease {
|
|
|
218
222
|
* @throws {ValidationError} Without a config.
|
|
219
223
|
*/
|
|
220
224
|
private requireConfig;
|
|
225
|
+
/** Load an explicit/discovered config, otherwise resolve one root project. */
|
|
226
|
+
private loadConfig;
|
|
221
227
|
/**
|
|
222
228
|
* Get the directory of the changeset files.
|
|
223
229
|
*
|
|
@@ -230,7 +236,7 @@ export declare class ChangesetRelease {
|
|
|
230
236
|
* Discover configured packages, pending changesets, and their proposed versions.
|
|
231
237
|
*
|
|
232
238
|
* @param input Repository root and optional explicit config file.
|
|
233
|
-
* @return A release plan; `undefined`
|
|
239
|
+
* @return A release plan; `undefined` without a config or a recognized root project.
|
|
234
240
|
*/
|
|
235
241
|
export declare function getChangesetReleasePlan(input: ChangesetRepository): Promise<ChangesetReleasePlan | undefined>;
|
|
236
242
|
/**
|