diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 901b20a..f83e57a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,3 +121,41 @@ jobs: echo "::error file=plugin.php::Version mismatch: package.json says ${npm_version}, plugin.php says ${plugin_version}." exit 1 fi + + # The action pins in the docs are what consumers copy, so a stale one + # quietly sends people to the previous release. This is the check that + # CONTRIBUTING.md used to ask a human to run by hand. + - name: The documented action pins name the current version + run: | + version="v$(node -p "require('./package.json').version")" + + stale="$(git grep -n 'wp-pattern-library@v' \ + -- README.md docs/05-github-action.md examples/ \ + | grep -v "wp-pattern-library@${version}" || true)" + + if [ -n "${stale}" ]; then + echo "::error::These action pins should read ${version}:" + echo "${stale}" + exit 1 + fi + + # The plugin writes the manifest and the CLI reads it. Their agreement is + # the one compatibility contract that is not the release version, and it + # otherwise surfaces only as a failed run in a consumer's repository. + - name: The manifest version the plugin writes is the one the CLI reads + run: | + php_version="$(sed -n "s/^const MANIFEST_VERSION = \([0-9]\+\);/\1/p" inc/manifest.php)" + js_version="$(sed -n "s/^const SUPPORTED_MANIFEST_VERSION = \([0-9]\+\);/\1/p" src/manifest.mjs)" + + echo "inc/manifest.php: ${php_version}" + echo "src/manifest.mjs: ${js_version}" + + if [ -z "${php_version}" ] || [ -z "${js_version}" ]; then + echo "::error::Could not read a manifest version from one of the pair; has a declaration been reformatted?" + exit 1 + fi + + if [ "${php_version}" != "${js_version}" ]; then + echo "::error file=src/manifest.mjs::Manifest version mismatch: the plugin writes ${php_version}, the CLI reads ${js_version}." + exit 1 + fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c0f49a..9e7d3fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -244,30 +244,32 @@ matter. Keep the two in step. Releases are cut from `main`. One tag drives all three artifacts, so the versions have to agree before the tag exists. -1. **Bump the version, in one commit.** Two of these are checked by CI, which - fails the pull request if they disagree. The other three are documentation, - and nothing will tell you if you forget them: - - | File | What to change | Checked? | - | ---- | -------------- | -------- | - | `package.json` | `version` | yes | - | `plugin.php` | the `Version:` header | yes | - | `README.md` | the `uses: humanmade/wp-pattern-library@vX.Y.Z` pin | no | - | `docs/05-github-action.md` | the same pin, twice | no | - | `examples/refresh-pattern-library.yml` | the same pin | no | +1. **Bump the version, in one commit.** All five are checked by CI, which fails + the pull request if they disagree: + + | File | What to change | + | ---- | -------------- | + | `package.json` | `version` | + | `plugin.php` | the `Version:` header | + | `README.md` | the `uses: humanmade/wp-pattern-library@vX.Y.Z` pin | + | `docs/05-github-action.md` | the same pin, twice | + | `examples/refresh-pattern-library.yml` | the same pin | The action pins are what consumers copy, so a stale one sends people to the - previous release. List all four and check they read as the new version: + previous release. To see all four before pushing: ```bash git grep -n "wp-pattern-library@v" -- README.md docs/05-github-action.md examples/ ``` + The action itself needs no bump. It runs the package version matching the ref + it was used at, read from `github.action_ref` at run time. + Then confirm nothing anywhere still names the version you bumped *from*. Substitute the old version; it should print no lines: ```bash - git grep -n "0\.3\.0" -- ':(exclude)package-lock.json' ':(exclude)composer.lock' + git grep -n "0\.4\.0" -- ':(exclude)package-lock.json' ':(exclude)composer.lock' ``` 2. **Merge to `main`.** 3. **Run the [Tag and Release](../../actions/workflows/tag-and-release.yml) diff --git a/README.md b/README.md index 67b3d4f..d51baee 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Three separate packages ship from this repository, all from the same tag: | ---------------- | ------------------------------- | ----------------------------------------------- | | WordPress plugin | `humanmade/wp-pattern-library` | `composer require humanmade/wp-pattern-library` | | CLI | `@humanmade/wp-pattern-library` | `npm install -D @humanmade/wp-pattern-library` | -| GitHub Action | `humanmade/wp-pattern-library` | `uses: humanmade/wp-pattern-library@v0.4.0` | +| GitHub Action | `humanmade/wp-pattern-library` | `uses: humanmade/wp-pattern-library@v0.4.1` | The plugin belongs on the site you capture from. The CLI runs wherever you generate the library — your machine, or CI — so the site itself never needs Node. The action is a thin wrapper around the CLI for running the whole thing in a workflow; pin it to a released tag, as there is deliberately no moving `@v1` tag while the package is pre-1.0. diff --git a/action.yml b/action.yml index 6252c4e..752793b 100644 --- a/action.yml +++ b/action.yml @@ -26,20 +26,65 @@ inputs: required: false default: '.' version: - description: Version of @humanmade/wp-pattern-library to run. + description: > + Version of @humanmade/wp-pattern-library to run. Defaults to the ref this + action was used at, so `uses: humanmade/wp-pattern-library@v1.2.3` runs + CLI 1.2.3 and the two can never drift apart. A branch or commit ref has no + version to read, and falls back to `latest`. required: false - default: 'latest' + default: '' runs: using: composite steps: - # Playwright needs a browser. The official container image ships one, so skip - # the install when the workflow already runs in it. + # The CLI is resolved before the browser, and into a directory of its own. + # + # Its own, because the project being documented may depend on a different + # Playwright, and its node_modules is not this action's to write into. + # + # Before, because the browser that gets installed below has to match the + # Playwright this install resolves. Anything that guesses at that match + # ahead of time — a container image tag, a warm npx cache — is correct only + # until Playwright cuts its next minor release. + - name: Install the CLI + shell: bash + env: + REQUESTED_VERSION: ${{ inputs.version }} + ACTION_REF: ${{ github.action_ref }} + run: | + set -euo pipefail + + version="${REQUESTED_VERSION}" + if [ -z "${version}" ]; then + case "${ACTION_REF}" in + v[0-9]*) version="${ACTION_REF#v}" ;; + *) version="latest" ;; + esac + fi + echo "Running @humanmade/wp-pattern-library@${version}" + + dir="${RUNNER_TEMP}/wp-pattern-library" + mkdir -p "${dir}" + cd "${dir}" + npm init --yes > /dev/null + npm install --no-audit --no-fund "@humanmade/wp-pattern-library@${version}" + + echo "${dir}/node_modules/.bin" >> "${GITHUB_PATH}" + + # Run from the CLI's tree, so `playwright` here is the one the CLI will + # load. --with-deps installs system libraries through apt, which needs sudo + # and a minute of network; the official Playwright image already has them + # and says so by exporting PLAYWRIGHT_BROWSERS_PATH. - name: Install Chromium shell: bash run: | - if [ -z "${PLAYWRIGHT_BROWSERS_PATH:-}" ] && [ ! -d "$HOME/.cache/ms-playwright" ]; then - npx --yes playwright install --with-deps chromium + set -euo pipefail + + cd "${RUNNER_TEMP}/wp-pattern-library" + if [ -n "${PLAYWRIGHT_BROWSERS_PATH:-}" ]; then + npx playwright install chromium + else + npx playwright install --with-deps chromium fi - name: Build the pattern library @@ -51,11 +96,17 @@ runs: PATTERN_LIBRARY_WP_APP_PASSWORD: ${{ inputs.app-password }} # Passed through the environment, never interpolated into the script: these # carry secrets, and a value with a newline or a quote in it would otherwise - # be run as shell. + # be run as shell. OUTPUT_PATH is not a secret, but the same reasoning + # applies to any input a caller controls. PATTERN_LIBRARY_EXTRA_HEADERS: ${{ inputs.extra-headers }} + OUTPUT_PATH: ${{ inputs.output-path }} run: | - args="" - if [ -n "${{ inputs.output-path }}" ]; then - args="--output-dir=${{ inputs.output-path }}" + set -euo pipefail + + # On PATH from the install step above, so this is the version resolved + # there rather than a second, independent resolution. + if [ -n "${OUTPUT_PATH}" ]; then + pattern-library build "--output-dir=${OUTPUT_PATH}" + else + pattern-library build fi - npx --yes @humanmade/wp-pattern-library@${{ inputs.version }} build $args diff --git a/docs/05-github-action.md b/docs/05-github-action.md index 2c2c298..516a759 100644 --- a/docs/05-github-action.md +++ b/docs/05-github-action.md @@ -80,9 +80,6 @@ permissions: jobs: refresh: runs-on: ubuntu-latest - # Ships Chromium and its system dependencies preinstalled, which saves a - # minute or so of apt traffic on every run. - container: mcr.microsoft.com/playwright:v1.62.1-noble steps: - uses: actions/checkout@v7 @@ -94,7 +91,7 @@ jobs: node-version: 24 - name: Build the pattern library - uses: humanmade/wp-pattern-library@v0.4.0 + uses: humanmade/wp-pattern-library@v0.4.1 with: site-url: ${{ vars.PATTERN_LIBRARY_SITE }} username: ${{ secrets.PATTERN_LIBRARY_WP_USER }} @@ -124,9 +121,26 @@ To commit directly to a branch instead of opening a pull request, drop the last | `output-path` | no | Overrides `outputDir` from the config file. | | `working-directory` | no | Where `pattern-library.config.js` lives. Default `.`. | | `extra-headers` | no | Headers for an origin behind an access proxy. | -| `version` | no | Version of the NPM package to run. Default `latest`. | +| `version` | no | Version of the NPM package to run. See below. | -Pin `version` to the same release as the action reference, so a run can't mix versions. Consuming workflows should pin the action to a released tag — there's deliberately no moving `@v1` tag while the package is pre-1.0. +Leave `version` alone. By default the action runs the package version matching +the ref it was used at, so `@v0.4.1` runs CLI 0.4.1 and the two cannot drift. +Set it only to test an unreleased package against a released action. A branch or +commit ref has no version to read and falls back to `latest`. + +Pin the action to a released tag — there's deliberately no moving `@v1` tag +while the package is pre-1.0. + +## Chromium + +The action installs Chromium itself, matched to the Playwright version the CLI +resolves. Nothing to configure, and nothing that goes stale. + +Running the job in `mcr.microsoft.com/playwright` is supported and saves the +system-library half of that install, but it is only ever an optimisation: the +image's own Chromium is used when its Playwright happens to match, and a +matching build is fetched when it doesn't. Pick the image tag to suit the +runner, not the CLI. ## Triggers @@ -234,7 +248,7 @@ Send the proxy's credentials alongside the application password: ```yaml - name: Build the pattern library - uses: humanmade/wp-pattern-library@v0.4.0 + uses: humanmade/wp-pattern-library@v0.4.1 with: site-url: ${{ vars.PATTERN_LIBRARY_SITE }} username: ${{ secrets.PATTERN_LIBRARY_WP_USER }} @@ -261,13 +275,13 @@ These headers go on the manifest request and on every browser request made **to ## Running it elsewhere -The action is a thin wrapper. The CLI is a plain Node program, so GitLab CI, Bitbucket Pipelines, Buildkite or a cron job on a box all work the same way: install Node 24 and Chromium, set the three environment variables, run `npx @humanmade/wp-pattern-library build`. +The action is a thin wrapper. The CLI is a plain Node program, so GitLab CI, Bitbucket Pipelines, Buildkite or a cron job on a box all work the same way: install Node 24, run `npx playwright install --with-deps chromium` from the same install tree as the package, set the three environment variables, and run `npx @humanmade/wp-pattern-library build`. ## Troubleshooting **The run captures the wrong site.** `PATTERN_LIBRARY_SITE` is a repository variable, so it's visible in the workflow log — check what the run actually printed. -**Chromium fails to install.** Use the `mcr.microsoft.com/playwright` container image, which ships it. The action skips its own install when it detects one. +**Chromium fails to install.** The install needs `apt` and passwordless `sudo` for its system libraries, which GitHub-hosted runners provide and a locked-down self-hosted runner may not. Run the job in the `mcr.microsoft.com/playwright` image, which ships those libraries; the action detects it and skips the `apt` half. **The pull request is enormous.** Every screenshot changed, which usually means live content in query loops. See [screenshot churn]({{ site.baseurl }}/npm-package#troubleshooting). diff --git a/examples/refresh-pattern-library.yml b/examples/refresh-pattern-library.yml index 519624d..43f84ed 100644 --- a/examples/refresh-pattern-library.yml +++ b/examples/refresh-pattern-library.yml @@ -39,9 +39,6 @@ permissions: jobs: refresh: runs-on: ubuntu-latest - # Ships Chromium and its system dependencies preinstalled, which saves a - # minute or so of apt traffic on every run. - container: mcr.microsoft.com/playwright:v1.62.1-noble steps: - uses: actions/checkout@v7 @@ -53,7 +50,7 @@ jobs: node-version: 24 - name: Build the pattern library - uses: humanmade/wp-pattern-library@v0.4.0 + uses: humanmade/wp-pattern-library@v0.4.1 with: site-url: ${{ inputs.site_url || vars.PATTERN_LIBRARY_SITE }} username: ${{ secrets.PATTERN_LIBRARY_WP_USER }} diff --git a/package-lock.json b/package-lock.json index 9f44411..2249a57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@humanmade/wp-pattern-library", - "version": "0.3.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@humanmade/wp-pattern-library", - "version": "0.3.0", + "version": "0.4.1", "license": "GPL-2.0-or-later", "dependencies": { "playwright": "^1.62.0", diff --git a/package.json b/package.json index 9760edb..139312f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@humanmade/wp-pattern-library", - "version": "0.4.0", + "version": "0.4.1", "description": "Generate a Markdown pattern library, with screenshots, from a WordPress site's registered block patterns.", "license": "GPL-2.0-or-later", "repository": { diff --git a/plugin.php b/plugin.php index 2c8806c..d342db1 100644 --- a/plugin.php +++ b/plugin.php @@ -2,7 +2,7 @@ /** * Plugin Name: WP Pattern Library * Description: Serves a manifest and isolated previews of the site's registered block patterns. - * Version: 0.4.0 + * Version: 0.4.1 * License: GPL-2.0-or-later * Requires PHP: 8.1 *