Contributing to wordpress-nginx
Thanks for helping. Bug reports, questions and pull requests are all welcome.
Where things go
- Bugs and feature requests: open an issue. For a bug, the chart version and the values that differ from the defaults usually decide where to look.
- Questions and ideas: Discussions.
- Security issues: never in a public issue — see SECURITY.md.
- The search sidecar the chart can deploy is developed in wordpress-idx. How the chart deploys it (the
idxvalues, its probes, its ingress path) is decided here; the search API and the search plugin are decided there. - WordPress itself, its plugins and themes belong to their own projects; the WordPress support forums are the place for questions about them.
What is where
| Path | What it holds |
|---|---|
chart/ | The Helm chart: templates/, values.yaml, its README.md and CHANGELOG.md, and its tests in tests/ |
chart/files/fim.php | The file-integrity watcher the chart can run next to WordPress |
docker/ | The image: Dockerfile, the files it adds in rootfs/, and its tests in tests/ |
examples/k3d/ | The page "Try it locally" and its values file |
tests/try-locally/ | The run of that page in a k3d cluster, and its guard test |
tests/ci/ | The tests of the workflows and of the scripts a release runs |
wordpress-idx/ | The search sidecar's source, a git submodule; a release builds the sidecar's image from it |
scripts/chart-version.sh | The version scheme, see Versions and releases |
scripts/ | Besides that, the scripts of the release workflow and the daily scan |
site/ | The documentation site, see The documentation site |
.github/workflows/ | The tests, the release build, the daily scan and the version trackers |
Running the tests
The tests are shell scripts. They need helm, docker, jq, yq (the Go one, not the Python wrapper of the same name) and Python 3 with PyYAML; the checks of the scripts and Dockerfiles need shellcheck and hadolint as well. Each block of commands starts in the repository root.
Chart
helm dependency update chart/
helm lint chart/
./chart/tests/template-test.sh
./chart/tests/schema-test.sh
./docker/tests/htaccess-test.sh
./chart/tests/fim-test.shhelm dependency updatefetches thecommonsubchart, which is not checked in. Everything below needs it.template-test.shrenders the chart with many sets of values, the ones inexamples/among them, and checks the manifests. It also checks that the version inchart/Chart.yamlfollows the scheme and agrees with the image tags next to it.schema-test.shcompareschart/values.schema.jsonwithchart/values.yaml: every documented value has its entry, with the same default. It also checks that the schema lets through the forms in which values reach the chart, and refuses what the chart cannot use.htaccess-test.shruns the init container's.htaccesscheck, taken from the rendered chart, in the image the init container uses.fim-test.shruns the file-integrity watcher against prepared volume trees, in a PHP container.
Image
docker build -t wordpress-nginx:test ./docker
./docker/tests/smoke-test.sh wordpress-nginx:testThe smoke test starts the image and probes it: the ports it listens on, the PHP execution allowlist, denied paths, the REST API hardening and the mail wrapper.
Workflows and release scripts
git submodule update --init
./tests/ci/test.sh
./tests/ci/shellcheck.sh
git ls-files -z '*Dockerfile' | xargs -0 hadolinttest.shholds every workflow to the project's rules (actions pinned by commit, a time limit per job, least permissions) and tests the scripts a release and the daily scan run, against a stand-in for the registry. It reads the search sidecar's version from the submodule and needs the chart's dependencies, like the chart tests.shellcheck.shruns shellcheck over every shell script,hadolintchecks the Dockerfiles.
Continuous integration runs all three blocks on every pull request, as the jobs chart-template-test, smoke-test, shellcheck and hadolint of .github/workflows/docker-test.yml.
Try it locally
tests/try-locally/test.sh
tests/try-locally/run.sh ./chartThe page Try it locally runs as written. test.sh checks in a second, without a cluster, that the page, its runner and its workflow still name the same things. run.sh creates the page's k3d cluster, runs every shell block of the page against the chart of the checkout and deletes the cluster again; it needs docker, k3d, kubectl, helm, curl and openssl, and the images the chart names in the registry (--import takes a locally built image instead, see the script's header). Continuous integration runs both on a pull request that changes the chart, the image, examples/k3d/ or these tests (.github/workflows/try-locally.yml).
Pull requests
A pull request is easiest to review when it:
- covers one change, with a test that fails without it: a case in
chart/tests/template-test.shfor the chart, indocker/tests/smoke-test.shfor the image, inchart/tests/fim-test.shfor the file-integrity watcher; - documents a new value with a
## @paramcomment inchart/values.yamland, where it needs more than a line, in the section ofchart/README.mdthat covers it, and gives it its entry inchart/values.schema.json, which is written by hand: type, description and the default ofvalues.yaml. A changed default is changed in both files../chart/tests/schema-test.shnames what is missing; keep the entry as permissive as its neighbours, since Helm refuses an install whose values the schema does not accept; - keeps the
<!-- #region name -->and<!-- #endregion name -->comments of the READMEs around their sections: the documentation site shows the sections by these names; - adds its lines under
## Unreleasedinchart/CHANGELOG.mdwhen it changes the chart or the image, and leaves the chart version alone, see Versions and releases; - uses the commit style of the history: a plain imperative summary (
Add …,Fix …,Let …), with the reason in the body. The commit of a release starts withVersion <chart version>:; - has every commit signed off, see Sign your commits (DCO).
Code, comments, documentation and commit messages are in English.
Before it is merged
A pull request is merged when all three hold:
- every check is green;
- CodeRabbit, which reviews every pull request, has approved its latest state;
- every review thread is resolved.
Security fixes are worded as hardening
A changelog entry, a commit message and a pull request say what is stricter or safer now and that upgrading is recommended. They do not describe how an earlier release could be attacked: installations upgrade at their own pace, and such a description would point at the ones that have not yet.
A vulnerability that is not public is reported privately first, not fixed in a public pull request; see SECURITY.md.
Versions and releases
The chart version is also the image tag. For WordPress x.y.z and build N it is x.y.P with P = z * 100 + N, so 7.1.408 is build 8 on WordPress 7.1.4; Chart versions has the scheme with examples. scripts/chart-version.sh next <chart version> prints the next build, scripts/chart-version.sh first <WordPress version> the first version for a new WordPress release.
A change to the chart or the image reaches users with the next chart version. The pull request that makes the change does not raise the version. It adds its lines under ## Unreleased at the top of chart/CHANGELOG.md, with that heading if it is not there yet, and says in them what a user of the chart notices.
A release is a pull request of its own. It
- sets
versioninchart/Chart.yaml; - sets the tags of the
wordpress-nginxand thewordpress-idxentry inartifacthub.io/imagesthere, which must equalversion: the chart version is the tag of both images; - turns
## Unreleasedinchart/CHANGELOG.mdinto the heading of that version, so that no line is left under## Unreleasedwhen it is merged:scripts/changelog-release.sh <version>.
./chart/tests/template-test.sh fails while the places in chart/Chart.yaml disagree, on a version outside the scheme, and when the version is not the newest one in the changelog. The templates take the image tags from the chart version itself.
After its merge the maintainers push a tag with the version's name on that commit, and the release workflow (.github/workflows/release.yml) runs:
- the tests, as a gate for everything that follows;
- both images, the WordPress image and the search sidecar's, built for each platform and pushed to the registry under their digests, with an SBOM and provenance, and the smoke test on the built WordPress image;
- the chart, packaged and pushed under its digest;
- one job that signs the two images and the chart;
- only then the tags: the version on both images and the chart, and
latest; - the GitHub release, with the changelog entry of the version as its notes, and "Try it locally" against what was published.
A failed job can be re-run. Verify a release has the commands that check the signatures afterwards.
Where a change calls for it, a release candidate comes first: the tag <version>-rc.1 on the same commit runs the whole release and publishes chart and images at the candidate's version, without moving latest. A fix goes to main by pull request, and the next candidate, -rc.2, on the new head. The release is then tagged on the commit its last candidate tested.
Tag the release pull request's merge commit by its hash, not whatever origin/main is by then, and push the tag as a step of its own:
git fetch origin
git merge-base --is-ancestor <commit> origin/main # the commit is on main
git rev-parse '<version>-rc.1^{commit}' # after a candidate: the same commit
git tag <version> <commit>
git push origin <version>A workflow asks wordpress.org every day for the current WordPress release. The pull request it opens for a new one is such a release pull request, and the three points hold for it as well: it runs the same script, so the lines under ## Unreleased stand under the new version's heading, below its line for the WordPress update. It also raises WP_VERSION in docker/Dockerfile and appVersion in chart/Chart.yaml, which the template test holds equal.
The search sidecar is the git submodule wordpress-idx; a release builds its image from the commit the submodule is pinned to. Moving the pin is a change to that image: a pull request with the new commit and a line under ## Unreleased that names the wordpress-idx version. After a fresh clone, git submodule update --init checks the submodule out.
Renovate opens the pull requests for new versions of GitHub Actions, the third-party images the chart pins and the site's packages (.github/renovate.json). A pull request that moves an image the chart pins needs its line under ## Unreleased as well; the bot's pull request says so.
The PHP series of the image is not Renovate's: the tag of the base image in docker/Dockerfile names it, and the maintainers move it by hand, because the plugins of a site depend on the PHP series. PHP patch releases need no change there; every release build pulls the base image afresh.
A tag is never moved and a version is never published a second time: a deployment that pins a version would not see the change. The release workflow refuses a version that is in the registry already. Any change after a release, however small, gets the next build number.
The documentation site
The site at wordpress-nginx.ioanalytica.com is built with VitePress from site/ and needs Node.js 22.18 or later:
cd site
npm ci --ignore-scripts
npm run dev # http://localhost:5173, reloads on every changeMost pages show sections of README.md and chart/README.md by region (<!-- #region name -->) instead of copying them, and chart/CHANGELOG.md, CONTRIBUTING.md, SECURITY.md and examples/k3d/README.md whole. So each text has one source: edit the file and the page follows.
A pull request that changes the site or a file it shows runs these in continuous integration:
site/tests/site-test.sh
(cd site && npm run build)
site/scripts/check-links.sh site/.vitepress/distSign your commits (DCO)
Every commit must carry a Signed-off-by line, certifying the Developer Certificate of Origin: that you wrote the change or otherwise have the right to submit it under the license of the part it changes. git commit -s adds the line from your git name and e-mail.
There is no contributor license agreement.
License
wordpress-nginx is licensed under the Apache License 2.0; NOTICE says what the license covers. A contribution is accepted under the license of the part it changes. WordPress itself, which the image contains, is licensed under the GNU General Public License, version 2 or later.
Conduct
Everyone taking part follows the code of conduct.