Release
A framework theme or plugin cannot simply be zipped from its Git checkout. The checkout has no vendor/, the editor scripts are not built, and the dependencies are not prefixed. A site with two framework consumers would load one framework for both of them.
wp-app-framework release turns a checkout into a ZIP that runs in any plain WordPress installation, without Composer on the server. It ships all dependencies, including the framework and Twig, prefixed with the project's own namespace in vendor-prefixed/. Several consumers with different framework versions can therefore run side by side.
In a consumer created as in Getting started, composer release runs the same command. It recognizes a theme or a plugin by the type in its composer.json.
What the release build does
The command works on a copy in the system's temporary directory and never changes your checkout. It runs these steps:
-
Copy the project. It copies the files Git does not ignore (
git ls-files --cached --others --exclude-standard), so uncommitted changes are included.generated/manifest.phpmust not exist in the checkout. -
Install the dependencies. It runs
composer install --no-dev --classmap-authoritativefrom the lock file, so the release contains exactly the versions you tested. -
Prefix the dependencies. Strauss renames every dependency into the project's namespace and rewrites the project's own code to use the new names. Strauss must be in the
PATH(the tools images of wp-dev-env and wp-test-env provide it); the release command runs it with PHP and a memory limit of 512 MB, since it parses the large class map of the dependencies.Then the command patches Twig. Twig's compiler writes class names such as
use Twig\Environment;as plain strings into every compiled template, and no prefixing tool rewrites strings.Release\TwigPatcherrewrites exactly these places. It expects each pattern a fixed number of times, so a Twig update that changes them fails the build instead of producing a broken release. -
Build the manifest. It builds
generated/manifest.phpwith the prefixed classes (usingbin/wp-app-framework-prefixed.phpin the prefixed copy) and writes the template version into it, and the image variants the templates ask for intogenerated/assets/(with Imagick or GD of the PHP that runs the release). Then it compiles all templates of the project and the framework, as release mode would, and checks the compiled code for names that were not prefixed. -
Build the assets. If there is a
package.json, it runsnpm ci && npm run build, which also builds the editor runtime fromvendor/. Then it checks that every built file the framework loads at run time exists, because a missing one would only fail on the page that needs it. The manifest decides which files these are:Static assets in
assets/and the image variants ingenerated/assets/are checked as well. A missing file stops the release with a list of the missing files and what needs them. A project without blocks, post meta, settings and stylesheet needs nopackage.json; one with them needs apackage.jsonwith abuildscript and awebpack.config.jswith the framework's webpack helper. -
Remove development files. It removes
node_modules/,vendor/,composer.json,composer.lock,package.json,package-lock.jsonand every path the project marksexport-ignorein.gitattributes(usedir/**for directories). Then it checks all PHP files for names of dependencies that were not prefixed, in string literals too. Insidevendor-prefixed/string literals are not checked, because dependencies mention their own class names in messages. -
Write the ZIP. It writes
dist/<slug>-<version>.zip, or the file given with--output, with the project directory as root. The slug is the directory name. The version is theVersion:header of the theme's or child theme'sstyle.css, or of the plugin's main file<slug>.php.--expect-versionmakes the build fail if the version differs, which CI uses to compare it with the tag.
A child theme is built without its parent theme. Calls of the parent's public template callables compile without it.
Every step fails with the file, command or setting at fault. On failure the build directory is kept, and its path is printed, so you can look at what went wrong.
Strauss settings
The project declares the Strauss settings in its composer.json. The command cannot add them itself, because extra is part of the lock file's content hash. It checks the values it relies on instead:
namespace_prefixandclassmap_prefixname the prefix for the dependencies. The prefix must not be the project's own namespace, which is Strauss' default.update_call_siteslists where Strauss rewrites your code to the prefixed names. It must contain the file that boots the framework:functions.phpof a theme or child theme,<slug>.phpof a plugin.- Leave
constant_prefixunset. It would rename the constants that the Symfony polyfills define for PHP itself, such asMB_CASE_UPPER. Twig defines no constants.
Unreleased framework changes
Normally the release uses the framework version from the lock file. To test a release with framework changes that are not released yet, replace the package with a local checkout:
--package-source=<package>=<directory> copies the checkout's files without those marked export-ignore, like a distribution archive. The output says that the build is not from the lock file. Composer path repositories are not supported, because the temporary copy has no sibling checkouts.
Distribution
The release ZIP is what you hand on – the source checkout never goes to a site. Where it goes depends on what you built.
A plugin that others may use belongs in the plugin directory of WordPress.org. You commit the content of the release ZIP to the plugin's SVN repository. The directory requires every file of a plugin to be under a license compatible with the GPL; the framework, Twig and the release build meet that, as long as your own code does too. Sites built with Bedrock then install the plugin with Composer from a mirror of the directory, such as repo.wp-packages.org, like any other plugin from WordPress.org.
A theme built with the framework cannot go into the theme directory of WordPress.org. Its review requirements forbid custom blocks and custom post types in themes, and blocks are what a framework theme is made of. A theme built for one site therefore lives in that site's repository – in a Bedrock site under web/app/themes/<slug> – and the deployment builds its release there:
The release command works in a subdirectory of another repository: it collects the files of the theme's directory with Git. The deployment then puts the content of the ZIP in place of the theme's source directory. The same applies to a plugin that only one site uses.
Rules:
- The release ZIP contains the directory
<slug>/with everything the theme or plugin needs; nothing is installed on the server. - The framework and its dependencies are licensed under the GPL-2.0-or-later; a theme or plugin that ships them must be GPL-compatible as well.
- Never deploy the source checkout: it runs in development mode, with unprefixed classes and without the release build's checks.