wp-plugin-base is a multi-host foundation for WordPress plugin repositories.
It is the delivery and governance layer for plugin repos:
- managed local GitHub or GitLab automation scaffolding
- release, packaging, and optional WordPress.org deployment automation
- workflow hardening and provenance checks
- vendored scripts, templates, and documentation under
.wp-plugin-base/
It is not a general plugin runtime framework. It does not provide plugin-side DI, PSR-4 runtime scaffolding, settings abstractions, REST controllers, or block architecture. Those concerns should remain outside this repo or move into a future companion runtime layer.
Each downstream project is expected to use one supported automation host profile:
- GitHub downstream repo
- GitLab downstream repo
Host-backed runtime updates follow that same downstream host:
AUTOMATION_PROVIDER=githubpairs withPLUGIN_RUNTIME_UPDATE_PROVIDER=github-releaseAUTOMATION_PROVIDER=gitlabpairs withPLUGIN_RUNTIME_UPDATE_PROVIDER=gitlab-releasePLUGIN_RUNTIME_UPDATE_PROVIDER=generic-jsonremains host-agnostic
The only normal cross-host case is the authoritative foundation release source: FOUNDATION_RELEASE_SOURCE_* may point at a different host because it describes where wp-plugin-base itself is officially published.
It also provides two reuse surfaces:
- managed repository files generated into your project (for example
.github/workflows/*on GitHub or.gitlab-ci.ymlon GitLab) - vendored source under
.wp-plugin-base/inside your project for scripts, templates, and documentation
The foundation is a development dependency only. The default baseline must never become a runtime dependency of the released plugin ZIP. A small, explicit opt-in runtime updater pack exists for GitHub Releases, GitLab Releases, or generic JSON metadata and is disabled by default.
The repository also enforces a tracked-file hygiene policy. Files such as .DS_Store, Thumbs.db, Desktop.ini, editor workspace folders, and transient debug logs are treated as forbidden repository content and fail validation if present.
wp-plugin-base is optimized first for product teams and maintainers who need:
- repeatable release automation across plugin repositories
- a hardened Git-host automation policy by default
- a vendored, reviewable infrastructure layer instead of opaque reusable workflows
- a clear update path for shared repo automation
If you only need a minimal plugin starter and do not want shared CI/release governance, wp scaffold plugin or a simpler starter is a better fit.
Default behavior is intentionally conservative. Optional channels and packs are opt-in.
| Capability | Type | Default | Enablement Surface |
|---|---|---|---|
| Selected Git-host release publication | Layer 1 core | GitHub by default | managed GitHub workflow or managed GitLab CI scaffold, depending on AUTOMATION_PROVIDER |
| WordPress.org deploy | distribution channel | disabled | CI variable WP_ORG_DEPLOY_ENABLED=true |
| WooCommerce.com deploy | distribution channel | disabled | CI variable WOOCOMMERCE_COM_DEPLOY_ENABLED=true + .wp-plugin-base.env WOOCOMMERCE_COM_PRODUCT_ID |
| Runtime updater pack | Layer 2 runtime pack | disabled | .wp-plugin-base.env PLUGIN_RUNTIME_UPDATE_PROVIDER + PLUGIN_RUNTIME_UPDATE_SOURCE_URL |
| REST operations pack | Layer 2 runtime pack | disabled | .wp-plugin-base.env REST_OPERATIONS_PACK_ENABLED=true |
| Admin UI pack | Layer 2 runtime pack | disabled | .wp-plugin-base.env ADMIN_UI_PACK_ENABLED=true |
| WooCommerce QIT workflow pack | optional workflow pack | disabled | .wp-plugin-base.env WOOCOMMERCE_QIT_ENABLED=true |
| Simulate release workflow | optional workflow pack | disabled | .wp-plugin-base.env SIMULATE_RELEASE_WORKFLOW_ENABLED=true |
- Vendor this repo into your plugin repository at
.wp-plugin-base/. - If this is a blank repo, create the plugin main file and
readme.txtbefore you sync. - Create
.wp-plugin-base.envfrom.wp-plugin-base/templates/child/.wp-plugin-base.env.example. - Fill in the required values.
- Run
bash .wp-plugin-base/scripts/update/sync_child_repo.sh. - Run
bash .wp-plugin-base/scripts/ci/validate_project.sh. - Commit
.wp-plugin-base/,.wp-plugin-base.env, and the generated managed files.
For the foundation repo itself, run:
bash scripts/dev/install_git_hooks.sh
bash scripts/foundation/validate.sh
bash scripts/foundation/bootstrap_strict_local.sh "$PWD/.wp-plugin-base-tools"
export PATH="$PWD/.wp-plugin-base-tools:$PATH"
bash scripts/foundation/validate.sh --mode strict-local
bash scripts/foundation/validate-full.shscripts/dev/install_git_hooks.sh configures core.hooksPath=.githooks so every git push executes both local workflow-equivalent validation paths before upload.
validate.sh defaults to fast-local mode, which tolerates missing foundation-only lint/security tools and reports reduced assurance explicitly. Use bash scripts/foundation/validate.sh --mode strict-local when you want the local run to fail if any required foundation lint/security tool is missing. validate-full.sh requires Docker and adds the WordPress readiness and Plugin Check fixtures on top. In CI the full suite skips rerunning the fast suite so the matrix does not pay for the same checks twice.
Fast local validation depends on these commands being available:
bashgitphpnoderubyperljqrsynczipunzip
rg is optional. The workflow auditor uses it when available and falls back to grep otherwise.
Full local validation and optional flows need additional tools:
ghfor GitHub release and pull request automationcurlfor GitLab release publication, repair, and API-backed update flowsdockerfor WordPress readiness validation, Plugin Check, and the full foundation validation suitepython3for WordPress.org deployment credential handlingsvnfor WordPress.org deploymentwpis not required locally; release-time POT generation uses the pinned@wordpress/envbundle whenPOT_FILEis configured
The shared scripts now fail fast with explicit missing-tool errors instead of failing deeper into release or update flows.
Foundation-only linting and release-security smoke checks use shellcheck, actionlint, yamllint, markdownlint-cli2, codespell, editorconfig-checker, gitleaks, syft, and cosign when they are installed locally. Foundation CI installs and runs them strictly even if they are absent on a contributor machine.
The repository pre-push hook runs:
bash scripts/foundation/validate.sh --mode fast-localbash scripts/foundation/validate-full.sh --mode fast-local
Set WP_PLUGIN_BASE_SKIP_LOCAL_PUSH_GATE=1 only when you intentionally need to bypass the local gate.
Use scripts/foundation/bootstrap_strict_local.sh to install the supported strict-local toolchain into .wp-plugin-base-tools. The bootstrap script installs pinned binary, Python, and Node-based tools with checksum, lockfile, or hash validation.
Foundation CI now installs the Node and Python lint toolchains from committed lock files and hash-pinned requirements. tools/wordpress-env remains a separate lockfile-backed npm tooling bundle, and shared scripts install it with npm ci --no-audit --no-fund from the committed package-lock.json.
To bootstrap strict-local foundation validation from a clean clone, use:
bash scripts/foundation/bootstrap_strict_local.sh "$PWD/.wp-plugin-base-tools"
export PATH="$PWD/.wp-plugin-base-tools:$PATH"
bash scripts/foundation/validate.sh --mode strict-localFor A+ release-readiness acceptance on a maintainer machine, install the strict-local tools and run the full strict suite in one step:
bash scripts/foundation/bootstrap_strict_local.sh "$PWD/.wp-plugin-base-tools" --validate-fullwp-plugin-base assumes a locked-down automation posture:
- workflows are local to your project and run against the checked-out repository
- every external action must be pinned to a full commit SHA
- only a small approved action allowlist is permitted
- custom project workflows stay read-only by default; privileged write flows remain managed
- release and update workflows use repo-local shell scripts where practical instead of additional third-party actions
- foundation self-updates only trust published foundation releases that pass provenance checks
GitHub is still the default automation host. GitLab is the other supported downstream host in this release. Gitea, Forgejo, and Bitbucket are not supported in this release.
See Security model for the full policy and the current approved action set.
For your project to consume this foundation successfully:
- your project must commit both
.wp-plugin-base/and.wp-plugin-base.envbefore the shared workflows can run - if you use automated foundation self-updates, your selected automation host must be able to read releases from
FOUNDATION_RELEASE_SOURCE_REFERENCE - if you want release preparation or foundation updates to open change requests automatically, your selected automation host must allow that bot identity to push branches and create PRs or MRs
If those conditions are not met, the local project workflows will either fail to find .wp-plugin-base/ or, for self-update only, fail to reach the foundation release source.
For GitHub-hosted repositories, enable pull request creation in GitHub:
- Open your repository on GitHub.
- Go to
Settings->Actions->General. - Scroll to
Workflow permissions. - Select
Read and write permissions. - Enable
Allow GitHub Actions to create and approve pull requests. - Save the changes.
If Allow GitHub Actions to create and approve pull requests is greyed out:
- Open the parent organization on GitHub.
- Go to
Settings->Actions->General. - Allow repositories in the organization to let GitHub Actions create and approve pull requests.
- Return to the repository and enable the repository-level setting there if GitHub still requires it.
See Troubleshooting for the failure modes and the organization-level case.
For GitLab-hosted repositories, configure a project or group token with the permissions needed to push release/update branches and open merge requests. When WordPress.org deploy is enabled on GitLab, local and CI validation fail closed until you explicitly acknowledge that the protected deployment environment has been reviewed manually.
Each project repository should contain:
.wp-plugin-base/populated from this repo as vendored source.wp-plugin-base.envwith project-specific metadata- plugin-local code and assets
- managed automation files for the selected host
Managed files are generated from templates/child/ by running:
bash .wp-plugin-base/scripts/update/sync_child_repo.shValidate the repo contract locally with:
bash .wp-plugin-base/scripts/ci/validate_project.shThat command enforces the generated managed-file surface, not just .github/workflows/*. A synced repo must keep the managed root files, the configured suppressions file, and any enabled quality/security/QIT pack files present as regular files.
You can bootstrap .wp-plugin-base/ with git subtree if you want that history locally, but the shared update workflow only requires a normal vendored copy.
If your plugin ships files from nested directories, keep PACKAGE_INCLUDE, PACKAGE_EXCLUDE, and DISTIGNORE_FILE as explicit repo-relative paths. Absolute paths are rejected. The default package excludes common development-only paths (/docs, /scripts, /tests, /packages, and /routes) so those workspaces stay out of the install ZIP and translation scan; include those directories explicitly only when they are part of the shipped plugin.
GitHub-managed repos receive .github/dependabot.yml and should keep Dependabot enabled so pinned action SHAs keep moving forward through normal review PRs. GitLab repos do not get a managed Dependabot equivalent in this release.
Managed child CI also runs a separate gitleaks secret-scan job by default. That job installs only the pinned scanner binary, scans the project checkout, and fails the workflow if secrets are detected.
Release publishing now emits three independent trust artifacts:
- GitHub build attestation for the released package
- CycloneDX SBOM for the packaged release contents
- Sigstore keyless bundle for the released blob
Use bash .wp-plugin-base/scripts/release/verify_sigstore_bundle.sh <owner/repo> <artifact-path> <bundle-path> plugin for strict consumer verification against the expected release workflows.
The foundation repository also runs an OpenSSF scorecard workflow on the default branch and publishes SARIF findings to GitHub code scanning.
It also includes a scheduled external dependency updater workflow at .github/workflows/update-plugin-check.yml (display name: update-external-dependencies). That workflow checks update candidates, refreshes pinned versions and hashes, and opens reviewable PRs through the same PR-governed update mechanism used by foundation updates. For WordPress/plugin-check, updates stay constrained to the current major series, published non-draft/non-prerelease releases, a reviewed release-author allowlist, and a 7-day stabilization window. External dependency PRs include a shared reviewer warning when first-party provenance cannot be verified automatically.
Apply this policy in GitHub under Settings -> Actions -> General for each project repository or, preferably, at the organization level:
- Under
Actions permissions, chooseAllow OWNER, and select non-OWNER, actions and reusable workflows. - Allow GitHub-authored actions.
- Allow only the specific non-GitHub actions that the current foundation version documents in Security model.
- Enable
Require actions to be pinned to a full-length commit SHA.
This foundation already generates workflows that match that policy. Keeping the GitHub setting aligned means GitHub rejects unexpected workflow drift before a compromised action can run.
Foundation releases use semver tags with a v prefix such as v1.0.1.
- your project pins
FOUNDATION_VERSIONto one exact foundation release - automated foundation update change requests only consider published releases from the configured authoritative foundation source, not arbitrary tags or branch heads
- automatic updates stay within the current major series
- major foundation upgrades are manual
If you are using an AI coding agent (or maintaining this repo as if it were one), treat these as hard invariants:
- Config key changes:
- update all four surfaces together:
scripts/lib/load_config.sh,docs/config-schema.json,README.mdconfig list, andtemplates/child/.wp-plugin-base.env.example - run
bash scripts/ci/validate_config_contract.sh
- update all four surfaces together:
- Managed workflow changes:
- keep reusable/root workflows and child templates in lockstep
- run
bash scripts/foundation/test_workflow_parity.sh
- Distribution/update channel changes:
- update workflow logic, docs, security model host allowlist (if network surface changed), and release/update fixtures
- run
bash scripts/foundation/run_release_update_fixture_checks.sh "$PWD"
- Dependency update automation changes:
- keep
docs/dependency-inventory.jsonin sync with.github/workflows/update-plugin-check.ymlandscripts/update/prepare_external_dependency_update.sh - run
bash scripts/ci/validate_dependency_inventory.shandbash scripts/foundation/test_dependency_inventory.sh
- keep
For a maintainer-oriented change map, see Maintainer and agent map.
Required keys in .wp-plugin-base.env:
FOUNDATION_RELEASE_SOURCE_PROVIDERFOUNDATION_RELEASE_SOURCE_REFERENCEFOUNDATION_VERSIONPLUGIN_NAMEPLUGIN_SLUGMAIN_PLUGIN_FILEREADME_FILEZIP_FILEPHP_VERSIONNODE_VERSION
Legacy compatibility alias: FOUNDATION_REPOSITORY remains accepted for GitHub-hosted foundations.
Optional keys:
FOUNDATION_RELEASE_SOURCE_API_BASEFOUNDATION_RELEASE_SOURCE_SIGSTORE_ISSUERFOUNDATION_REPOSITORYAUTOMATION_PROVIDERAUTOMATION_API_BASETRUSTED_GIT_HOSTSPLUGIN_RUNTIME_UPDATE_PROVIDERPLUGIN_RUNTIME_UPDATE_SOURCE_URLPHP_RUNTIME_MATRIXPHP_RUNTIME_MATRIX_MODEPHPSTAN_MEMORY_LIMITVERSION_CONSTANT_NAMEPOT_FILEPOT_PROJECT_NAMEWORDPRESS_ORG_SLUGWORDPRESS_READINESS_ENABLEDWORDPRESS_QUALITY_PACK_ENABLEDWORDPRESS_SECURITY_PACK_ENABLEDRELEASE_READINESS_MODEWOOCOMMERCE_QIT_ENABLEDWOOCOMMERCE_COM_PRODUCT_IDWOOCOMMERCE_COM_ENDPOINT_TIMEOUT_SECONDSGITHUB_RELEASE_UPDATER_ENABLEDGITHUB_RELEASE_UPDATER_REPO_URLREST_OPERATIONS_PACK_ENABLEDREST_API_NAMESPACEREST_ABILITIES_ENABLEDADMIN_UI_PACK_ENABLEDADMIN_UI_STARTERADMIN_UI_EXPERIMENTAL_DATAVIEWSADMIN_UI_NPM_AUDIT_LEVELWP_PLUGIN_BASE_PLUGIN_CHECK_CHECKSWP_PLUGIN_BASE_PLUGIN_CHECK_EXCLUDE_CHECKSWP_PLUGIN_BASE_PLUGIN_CHECK_CATEGORIESWP_PLUGIN_BASE_PLUGIN_CHECK_IGNORE_CODESWP_PLUGIN_BASE_PLUGIN_CHECK_STRICT_WARNINGSWP_PLUGIN_BASE_PLUGIN_CHECK_SEVERITYWP_PLUGIN_BASE_PLUGIN_CHECK_ERROR_SEVERITYWP_PLUGIN_BASE_PLUGIN_CHECK_WARNING_SEVERITYEXTRA_ALLOWED_HOSTSWP_PLUGIN_BASE_SECURITY_SUPPRESSIONS_FILEPACKAGE_INCLUDEPACKAGE_EXCLUDEDISTIGNORE_FILEBUILD_SCRIPTBUILD_SCRIPT_ARGSPHPDOC_VERSION_REPLACEMENT_ENABLEDPHPDOC_VERSION_PLACEHOLDERCHANGELOG_MD_SYNC_ENABLEDCHANGELOG_SOURCESIMULATE_RELEASE_WORKFLOW_ENABLEDGLOTPRESS_TRIGGER_ENABLEDGLOTPRESS_URLGLOTPRESS_PROJECT_SLUGGLOTPRESS_FAIL_ON_ERRORDEPLOY_NOTIFICATION_ENABLEDCHANGELOG_HEADINGPRODUCTION_ENVIRONMENTCODEOWNERS_REVIEWERS
Use shell-safe KEY=value syntax. Quote values that contain spaces, for example PLUGIN_NAME="Example Plugin". ZIP_FILE must be a simple .zip filename, not a path.
.wp-plugin-base.env is a file committed in your project repository. It is not a CI variable on GitHub or GitLab.
The canonical machine-readable config contract is tracked in docs/config-schema.json. Foundation validation enforces parity between that schema, load_config.sh, this README key list, and templates/child/.wp-plugin-base.env.example.
PACKAGE_INCLUDE, PACKAGE_EXCLUDE, DISTIGNORE_FILE, and WP_PLUGIN_BASE_SECURITY_SUPPRESSIONS_FILE must stay repo-relative. DISTIGNORE_FILE must point to a *.distignore file. PRODUCTION_ENVIRONMENT defaults to production when unset.
BUILD_SCRIPT must be a repo-relative script path. When set, build_zip.sh runs it from the repository root before staging files. BUILD_SCRIPT_ARGS is an optional comma-separated argument list passed to that script.
PHPDOC_VERSION_REPLACEMENT_ENABLED=true enables release-time replacement of @since <placeholder> and @version <placeholder> in tracked PHP files. Set PHPDOC_VERSION_PLACEHOLDER to customize the token (default NEXT).
CHANGELOG_MD_SYNC_ENABLED=true mirrors generated release notes into CHANGELOG.md when the file uses ## x.y.z or ## vx.y.z headings. Unknown heading layouts are left untouched.
CHANGELOG_SOURCE=commits preserves the existing commit-subject changelog generation. Set CHANGELOG_SOURCE=change_request_titles to generate notes from merged PR or MR titles using the selected automation host metadata. CHANGELOG_SOURCE=prs_titles remains accepted as a legacy alias and is normalized to change_request_titles.
SIMULATE_RELEASE_WORKFLOW_ENABLED=true includes an optional managed simulate-release.yml workflow for dry-run release previews.
GLOTPRESS_TRIGGER_ENABLED=true enables a post-release GlotPress import trigger via GLOTPRESS_URL and GLOTPRESS_PROJECT_SLUG. GLOTPRESS_FAIL_ON_ERROR=true makes trigger failures fail the workflow; default behavior logs a warning and continues.
DEPLOY_NOTIFICATION_ENABLED=true enables post-release webhook notifications. The webhook URL must come from DEPLOY_NOTIFICATION_WEBHOOK_URL GitHub secret and failures are non-blocking by default.
Because the webhook destination is secret-sourced at runtime, workflow audit host allowlisting cannot statically validate that destination.
Set CODEOWNERS_REVIEWERS only if you want the generated project files to include a managed CODEOWNERS file for the selected host. Use one or more reviewer handles or teams separated by spaces, for example CODEOWNERS_REVIEWERS="@your-org/platform @your-user".
TRUSTED_GIT_HOSTS allows explicitly trusted Git API/auth hosts for self-managed GitLab or GitHub Enterprise instances. Use hostnames only. Private-network, link-local, localhost, and *.internal hosts are rejected.
FOUNDATION_RELEASE_SOURCE_SIGSTORE_ISSUER is only needed for self-managed GitLab foundation sources. gitlab.com uses its standard issuer automatically; self-managed GitLab must set the issuer explicitly.
WORDPRESS_QUALITY_PACK_ENABLED=true enables the broader PHP quality pack during WordPress readiness validation. It is a readiness submode and therefore requires WORDPRESS_READINESS_ENABLED=true. Full quality-pack mode manages PHPCS/PHPStan/PHPUnit support files, and seeds a child-owned phpstan.neon overlay for project-specific paths, excludes, bootstrap files, and scan files.
WORDPRESS_SECURITY_PACK_ENABLED=true enables a narrower security-focused pack during WordPress readiness validation. It is a readiness submode and therefore requires WORDPRESS_READINESS_ENABLED=true. That pack runs explicit WordPress.Security, WordPress.DB, and WordPress.WP.Capabilities sniffs, blocks risky public endpoint patterns, and audits root Composer/npm runtime dependencies when lock files are present.
RELEASE_READINESS_MODE=security-sensitive is an opt-in fail-closed release profile for plugins with elevated security requirements. It requires WORDPRESS_READINESS_ENABLED=true, WORDPRESS_QUALITY_PACK_ENABLED=true, WORDPRESS_SECURITY_PACK_ENABLED=true, strict Plugin Check warnings, full Plugin Check coverage without check/category/ignore/severity filters, and the default high admin UI npm audit threshold.
Use .wp-plugin-base-security-suppressions.json (or set WP_PLUGIN_BASE_SECURITY_SUPPRESSIONS_FILE) to declare intentional public endpoint exceptions with mandatory justification.
If POT_FILE is configured, release preparation generates it when it is missing. The path still needs to stay inside the repository and point at a writable location. Translation support also requires a Domain Path plugin header, typically /languages/, when POT_FILE is configured or a languages/ directory is present.
Workflow files use the .yml extension. .yaml workflow files are rejected by project and foundation validation.
WP_PLUGIN_BASE_PLUGIN_CHECK_* keys provide optional policy controls for Plugin Check execution during WordPress readiness validation:
..._CHECKSruns only specific checks...._EXCLUDE_CHECKSexcludes specific checks...._CATEGORIESfilters checks by categories such asplugin_repo,security...._IGNORE_CODESignores specific Plugin Check result codes...._STRICT_WARNINGS=truefails readiness validation on warnings in addition to errors...._SEVERITY,..._ERROR_SEVERITY, and..._WARNING_SEVERITYpass through severity thresholds to Plugin Check.
ADMIN_UI_NPM_AUDIT_LEVEL controls the managed admin UI npm audit threshold when the security pack is enabled. Keep the default high for release readiness. critical is only allowed outside RELEASE_READINESS_MODE=security-sensitive as a temporary compatibility override for non-runtime, upstream-owned admin UI toolchain advisories while you update @wordpress/* packages or add narrow npm overrides.
PHP_RUNTIME_MATRIX enables an additional CI smoke job across the listed interpreter versions, for example PHP_RUNTIME_MATRIX=8.1,8.2,8.3. The matrix reruns repository validation, WordPress metadata checks, and a direct main-plugin load smoke with each configured PHP version. Set PHP_RUNTIME_MATRIX_MODE=strict to also run PHPUnit in the matrix when phpunit.xml.dist and the managed quality-pack tool bundle are present.
PHPSTAN_MEMORY_LIMIT optionally passes a memory limit to the managed PHPStan command, for example 768M, 1G, or -1. Keep analysis paths, excludes, bootstrap files, and scan files in the child-owned phpstan.neon overlay so sync can update phpstan.neon.dist without taking over project-specific analysis scope.
PHP quality-pack and runtime-matrix behavior matrix:
WORDPRESS_QUALITY_PACK_ENABLED |
PHP_RUNTIME_MATRIX |
PHP_RUNTIME_MATRIX_MODE |
Managed PHPUnit bridge files (phpunit.xml.dist, tests/bootstrap.php, .wp-plugin-base-quality-pack/**, tests/wp-plugin-base/*Test.php) |
Full pack-only files (.phpcs.xml.dist, phpstan.neon.dist) and seeded child-owned files (phpstan.neon) |
|---|---|---|---|---|
false |
unset | smoke (default) |
no | no |
false |
set | smoke |
no | no |
false |
set | strict |
yes | no |
true |
unset or set | smoke or strict |
yes | yes |
Strict runtime matrix mode can therefore manage and execute the PHPUnit bridge even when the full quality pack is disabled. This is expected behavior, not a framework gap.
When the PHPUnit bridge is active, treat tests/bootstrap.php as managed and keep child-specific preload/support-class wiring in tests/wp-plugin-base/bootstrap-child.php. The managed phpunit.xml.dist discovers *Test.php files under tests/, so project tests can live in a child-owned path such as tests/php. During migration, move custom preloads there before or immediately after sync to avoid post-sync CI regressions.
WOOCOMMERCE_QIT_ENABLED=true syncs an optional manual WooCommerce QIT workflow into the child repository. That workflow is intended for WooCommerce Marketplace/partner use, expects QIT_USER and QIT_APP_PASSWORD secrets plus a manually provided WooCommerce extension slug, and uses a pinned internal woocommerce/qit-cli version.
WOOCOMMERCE_COM_PRODUCT_ID enables WooCommerce.com Marketplace release deploy preflight and upload when the CI variable WOOCOMMERCE_COM_DEPLOY_ENABLED=true is set. Keep WOO_COM_USERNAME and WOO_COM_APP_PASSWORD in protected CI secrets. Leave the product ID empty during Woo onboarding approval and the release flow soft-skips Woo deploy.
WOOCOMMERCE_COM_ENDPOINT_TIMEOUT_SECONDS controls WooCommerce.com API request timeouts for deploy and status checks (default 30 seconds).
PLUGIN_RUNTIME_UPDATE_PROVIDER=github-release|gitlab-release|generic-json enables an opt-in runtime pack that ships YahnisElsts Plugin Update Checker in lib/wp-plugin-base/plugin-update-checker/ and a managed bootstrap in lib/wp-plugin-base/wp-plugin-base-runtime-updater.php. Set PLUGIN_RUNTIME_UPDATE_SOURCE_URL to the matching repository or JSON metadata URL and add require_once __DIR__ . '/lib/wp-plugin-base/wp-plugin-base-runtime-updater.php'; to the plugin main file. github-release requires AUTOMATION_PROVIDER=github. gitlab-release requires AUTOMATION_PROVIDER=gitlab. generic-json is host-agnostic, but it is a runtime updater transport only, not a supported FOUNDATION_RELEASE_SOURCE_PROVIDER or native source contract for managed downstream automation. Systems such as wp-core-base should keep consuming the authoritative Git host release surface. Runtime update URLs must be public HTTPS URLs without credentials, query strings, fragments, localhost/private-network hosts, or token-like material. GITHUB_RELEASE_UPDATER_ENABLED and GITHUB_RELEASE_UPDATER_REPO_URL remain accepted as GitHub-only compatibility aliases.
REST_OPERATIONS_PACK_ENABLED=true enables an opt-in runtime pack that manages a shared REST operation registry and adapters in lib/wp-plugin-base/rest-operations/ while seeding child-owned examples in includes/rest-operations/. Set REST_API_NAMESPACE=<plugin-slug>/v1 to override the default namespace and add require_once __DIR__ . '/lib/wp-plugin-base/rest-operations/bootstrap.php'; to the plugin main file.
REST_ABILITIES_ENABLED=true enables the managed Abilities adapter for the REST operations pack when the target WordPress runtime exposes the Abilities API.
Disabling REST_OPERATIONS_PACK_ENABLED is also a manual reconciliation step. Sync removes the managed bootstrap, but you must remove the child-owned require_once __DIR__ . '/lib/wp-plugin-base/rest-operations/bootstrap.php'; include before validation or packaging will pass.
ADMIN_UI_PACK_ENABLED=true enables an opt-in runtime pack that manages a shared admin UI bootstrap in lib/wp-plugin-base/admin-ui/, seeds child-owned app sources in .wp-plugin-base-admin-ui/, and expects BUILD_SCRIPT=.wp-plugin-base-admin-ui/build.sh. Add require_once __DIR__ . '/lib/wp-plugin-base/admin-ui/bootstrap.php'; to the plugin main file. The admin UI pack also requires REST_OPERATIONS_PACK_ENABLED=true.
ADMIN_UI_STARTER=basic|dataviews selects which admin starter is seeded when the admin UI pack is enabled. basic is the default lighter component-only starter. dataviews seeds the DataForm/DataViews starter.
ADMIN_UI_EXPERIMENTAL_DATAVIEWS=true remains supported as a backward-compatible alias for ADMIN_UI_STARTER=dataviews.
The admin starter files are child-owned and seeded once. Changing ADMIN_UI_STARTER after the first sync does not rewrite those files; validate_project.sh will fail until the child-owned starter is reconciled manually or re-seeded intentionally.
Disabling ADMIN_UI_PACK_ENABLED is also a manual reconciliation step. Sync removes the managed bootstrap, but you must remove the child-owned require_once __DIR__ . '/lib/wp-plugin-base/admin-ui/bootstrap.php'; include, clear BUILD_SCRIPT=.wp-plugin-base-admin-ui/build.sh, and delete any stale assets/admin-ui/ build outputs before validation or packaging will pass. Deleting the seeded .wp-plugin-base-admin-ui/ sources is optional but recommended once the pack is intentionally removed.
EXTRA_ALLOWED_HOSTS allows additional outbound URL hosts for workflow/script audit policy (comma-separated hostnames only). Localhost, private-network, link-local, single-label, and *.internal hosts are rejected; keep this list minimal.
Local validate.sh defaults to fast-local mode. That mode proves the release tooling wiring and reports any checks that were skipped when local prerequisites are unavailable. Use --mode strict-local after bootstrap_strict_local.sh for CI-like tool enforcement on a contributor machine. GitHub foundation-ci runs validate.sh --mode ci and is the authoritative strict execution path for GitHub Actions OIDC-sensitive Sigstore signing checks.
WordPress.org deploy is built into the shared release workflow and is disabled by default.
To enable it in your project:
- set
WP_ORG_DEPLOY_ENABLED=truein the selected CI host - set
WORDPRESS_ORG_SLUGin.wp-plugin-base.env - provide
SVN_USERNAMEandSVN_PASSWORDas protected CI secrets on that host
If WP_ORG_DEPLOY_ENABLED is unset or any value other than true, the release workflow skips SVN deploy.
Release publication uses host-release-first ordering: the selected Git host release publishes first, then enabled distribution channels (WordPress.org and WooCommerce.com) run post-publish.
WordPress.org can therefore fail after the selected Git host release is already public.
Repair runbook after publication:
- GitHub stable release: run the manual
release.ymlworkflow for the existing tag, then runwoocommerce-status.ymlwhen WooCommerce.com is enabled - GitHub prerelease: push or rerun the trusted prerelease tag so
publish-tag-release.ymlcreates or repairs the prerelease GitHub Release - GitLab: rerun the tagged
releasejob from the managed.gitlab-ci.yml; there is no separatewoocommerce-status.ymlworkflow on GitLab, so inspect Woo vendor/QIT status directly when that channel is enabled
If you are migrating from older internal release ordering where WordPress.org deploy blocked tag publication, treat this as a behavior change and update release runbooks.
For stronger review on production publishing, protect the deployment environment named by PRODUCTION_ENVIRONMENT and require at least one reviewer before the workflow can access deploy credentials. GitHub validation checks this automatically. GitLab validation fails closed until you rerun with WP_PLUGIN_BASE_GITLAB_DEPLOY_ENV_ACKNOWLEDGED=true after manually reviewing the protected environment rules.
Repair flows skip WordPress.org redeploy by default so an existing tags/<version> entry is not mutated during a repair run. On GitHub that behavior lives in the manual release.yml workflow for stable tags and the prerelease-only publish-tag-release.yml workflow for trusted prerelease tags. On GitLab it lives in the tagged release job from the managed .gitlab-ci.yml. Only set the repository or environment variable WP_PLUGIN_BASE_ALLOW_WPORG_TAG_REDEPLOY=true for an intentional break-glass redeploy of the latest repository release tag.
- New project setup
- Existing project migration
- Product layers
- Release model
- WooCommerce.com distribution
- Runtime in-dashboard updater
- Security model
- Secure plugin coding contract
- Compatibility and public contract
- Future enhancements
- Foundation release process
- Changelog policy
- PR-based changelog notes
- Update model
- Troubleshooting
- Maintainer and agent map