Releases and CI
Releasing a theme or plugin should be a single step: push a tag. The framework provides a GitHub Actions workflow that does the rest. It builds the release ZIP, installs it into a plain WordPress, runs your browser tests against it, and attaches the ZIP to the GitHub release only if everything passed. A ZIP that fails its tests is never published.
Tags follow semantic versioning with a v prefix: v1.2.0.
Themes, child themes and plugins
Your repository calls the reusable workflow .github/workflows/consumer-release.yml of the framework repository from a workflow of its own. That workflow runs on push: tags: ['v*'] and needs permissions: contents: write to attach the ZIP. The test projects theme-dev-test, theme-dev-test-child and plugin-dev-test contain working examples to copy.
Pin the workflow to a framework tag, so a new framework version never changes your release process unannounced.
The workflow:
- checks out the project and wp-test-env, the test environment it runs in;
- runs
composer lintandnpm run lint, if they are defined, andwp-app-framework build; - builds the release; when it publishes, with
--expect-versionset to the version of the tag, so a tag that doesn't matchVersion:fails; - installs the ZIP into fresh, plain WordPress instances of wp-test-env, with the plugin and theme directories read-only;
- with the input
cli: true, runs the project's WP-CLI tests against the installed release (see below); - runs the project's browser tests (
npm run test:e2e) withE2E_TARGET=release, one Playwright worker per instance (inpute2e-instances, default 2); the inpute2e: falseskips them.
Plugins and child themes need a theme
A theme can be tested on its own, but a plugin or a child theme needs a theme to run in. The workflow decides from the type in composer.json and the Template: header of style.css:
- a plugin is installed next to the release ZIP of a theme;
- a child theme is installed on top of the release ZIP of its parent theme.
The input with-theme: <owner>/<repository>@<tag> names that theme release. It is required for plugins and child themes.
WP-CLI tests
Browser tests can't run WP-CLI commands. With cli: true, the workflow runs the executable tests/cli/run of your project after the release is installed. Its only argument is the name of the wp-test-env instance with the release; wp-test-env is checked out next to your project. The script runs your commands with ../wp-test-env/bin/wp "$1" … (relative to your project) and fails with a non-zero exit code if an output or exit code is not what you expect. During development, run the same script against an instance of wp-test-env in development mode.
Browser tests
The workflow runs the browser tests with bin/e2e of wp-test-env: npm run test:e2e of your project in the Playwright image of its @playwright/test version. Every instance gets its own Playwright worker, so tests of different workers never share a site. Your Playwright configuration receives the instance URLs in WP_BASE_URLS (comma-separated), the administrator in WP_USERNAME and WP_PASSWORD, E2E_TARGET and a writable results directory in E2E_OUTPUT_DIR; it sets workers to the number of URLs and lets each worker pick its URL by TEST_PARALLEL_INDEX. The README of wp-test-env explains the contract; the configurations of the test projects are working examples.
Inputs
The framework
The framework repository tests itself in two workflows:
ci.ymlruns PHPUnit, Vitest and all linters on pushes and pull requests, and builds this documentation.release.ymlruns on a tag. It runs the same checks and then the release gates: theme-dev-test, plugin-dev-test and theme-dev-test-child are built with the framework of the tag (framework-ref) and tested in the browser, the plugin and the child theme with an older release of the theme, without publishing anything. Only then does it attachwp-app-framework-<version>.zipto the GitHub release – made withgit archive, without the development files markedexport-ignore– and publish the documentation of the tag's minor version.
The gates make sure that a framework release works for a theme, a plugin and a child theme before anyone can depend on it.
Requiring the framework
The framework is on Packagist. Packagist reads the tags of the GitHub repository and updates itself on every push, so a release can be required as soon as its tag exists. Themes and plugins require a version range, and the lock file pins the exact version:
The test projects of the framework require dev-main instead, so they always build with the latest framework.