⏯️ Quibble is for setting up a MediaWiki instance and running various tests against it.
A GitHub composite action that runs Quibble against a MediaWiki extension or skin. It mirrors what Wikimedia CI does on Gerrit, but on GitHub Actions: it clones MediaWiki core and your declared dependencies, sets everything up inside the official Wikimedia Quibble Docker images, and runs the test stage you ask for. It can also run Phan static analysis and PHPUnit code coverage.
- Detects the project. The action reads
extension.jsonorskin.jsonfrom the checked-out repository to decide whether it is testing an extension or a skin, and under which name. When neither file is present it falls back to the Vector skin. - Resolves dependencies. Dependency extensions and skins are read from the
dependenciesinput, therequiresclause ofextension.json/skin.json, or the phan config, and cloned into place. See Defining dependencies. - Restores caches. The Docker images, the MediaWiki checkout, and the Composer cache are all cached between runs.
- Runs the stage. Quibble runs the requested stage inside the Docker image, or the action runs Phan or coverage for those two modes.
Add a workflow to your extension or skin repository:
name: Test
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: femiwiki/quibble-action@dc8d9ec9d6c86ba9805a77736c68f974d250aa8f # v1.0.0By default the all stage runs. Set stage to run a single Quibble stage, or
one of the two extra modes this action adds:
- uses: femiwiki/quibble-action@dc8d9ec9d6c86ba9805a77736c68f974d250aa8f # v1.0.0
with:
stage: phpunit- any stage from the Quibble stages documentation (for example
phpunit,selenium,qunit); phanruns Phan static analysis instead of Quibble, and reports each issue as an inline annotation on the offending line in the pull request. How that is produced depends on the project's phan version: phan >= 5.5 has a native--output-mode=github(used directly, no extra tooling), while older phan emits--output-mode=checkstylewhich is piped through cs2pr for the same annotations. The version is detected from the project'sphan/phanentry, so either way works with no configuration;coverageruns PHPUnit code coverage and exposes the report through thecoverageoutput. It requiresmediawiki-version: master, because MediaWiki's coverage tooling (tests/phpunit/generatePHPUnitConfig.php) currently lives only in the master branch; on other branches it is skipped. The stage fails when the suite it ran did not pass. The action reads the JUnit report the run left behind and exits non-zero on any failure, any error, a report it cannot read, or a run that collected no tests at all, so that a broken test cannot sit in a green coverage job. That last condition is worth knowing about: a project whosetests/phpunitdirectory exists but yields nothing under theextensionstest suite now fails where it used to pass in silence. See Publishing coverage for what failing means for the report itself.
Dependency extensions and skins are resolved from the first of these sources that yields anything:
-
The
dependenciesinput — a whitespace/comma separated list:with: dependencies: Foo Bar skins/Vector
Entries may be bare names (
Foo→mediawiki/extensions/Foo), short prefixed paths (skins/Vector), or full Gerrit paths. -
The
requiresclause ofextension.json/skin.json— therequires.extensionsandrequires.skinskeys. -
The phan config (
.phan/config.php) —../../extensions/<Name>and../../skins/<Name>entries in the directory/file list.
Use exclude-dependencies to drop specific resolved entries by name.
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
mediawiki-version: [REL1_45, master]
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: femiwiki/quibble-action@dc8d9ec9d6c86ba9805a77736c68f974d250aa8f # v1.0.0
with:
mediawiki-version: ${{ matrix.mediawiki-version }}By default the action treats the workspace root as the project under test, so a
consumer just checks out their repository and runs the action. When the action
itself lives at the workspace root, for example to test it as uses: ./, check
the project under test out into a subdirectory and point project-path at it:
# The action under test at the workspace root, so it can be `uses: ./`.
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# The extension or skin under test in a subdirectory.
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: my-org/MyExtension
path: project
- uses: ./
with:
project-path: project - id: quibble
uses: femiwiki/quibble-action@dc8d9ec9d6c86ba9805a77736c68f974d250aa8f # v1.0.0
with:
stage: coverage
mediawiki-version: master
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage
path: ${{ steps.quibble.outputs.coverage }}MediaWiki's mwext-phpunit-coverage drops PHPUnit's exit code on purpose, so
that a run with some failing tests still publishes the coverage of the ones
that passed. Wikimedia CI can afford that because its separate PHPUnit jobs
already gate the tests themselves; a workflow that only runs the coverage
stage has no such second opinion, and a failing suite would sit in a green job.
The stage therefore fails, but only after the report has been written: it is
complete on disk and outputs.coverage still points at it. To keep publishing
it from a run whose tests failed, mark the publishing step if: always().
Every stage runs in an official Wikimedia image, pulled as
<docker-registry>/<docker-org>/<image>:<tag>. By default the <image> is
derived from php-version (and debian for Quibble), so you usually only set
those two knobs. Any image can also be pinned explicitly with its
*-docker-image input, which takes precedence over the derivation.
| Stage | Image | Override input |
|---|---|---|
Quibble (all and individual stages) |
quibble-<debian>-php<version> |
quibble-docker-image |
coverage |
quibble-coverage |
coverage-docker-image |
phan |
mediawiki-phan-testrun |
phan-docker-image |
coverage is not derived from debian/php-version: it uses the single
quibble-coverage image (pcov-based, the one Wikimedia CI uses), which replaced
the old per-PHP coverage images.
phan runs in mediawiki-phan-testrun, the image Wikimedia CI runs phan in.
The old standalone mediawiki-phan-php<version> images were frozen at php-ast
1.1.2 in 2025-07 and cannot run phan >= 6 (current mediawiki-phan-config),
which needs php-ast 1.1.3+; mediawiki-phan-testrun is rebuilt and ships a
current php-ast. It is a single image (no php<version> variants), so
php-version and debian do not affect it. The project's phan dependencies are
installed in the Quibble image first, then phan runs over them in
mediawiki-phan-testrun (--entrypoint bash … vendor/bin/phan).
When php-version is empty it is derived from mediawiki-version, with two
policies:
- Most stages, including
phan, use each branch's minimum PHP, to test the floor:8.1for REL1_43/REL1_44,8.2for REL1_45,8.3for REL1_46 and master,8.4otherwise. Forphanthis only selects the Quibble image that installs its dependencies (phan itself runs inmediawiki-phan-testrun); the minimum already clears phan's floor (phan 6 needs PHP 8.1+, phan 5 less), so it needs no higher version of its own. api-testingalways uses8.3: it needs the wikidiff2 PHP extension, and the only published image bundling it isquibble-bookworm-php83.
MediaWiki releases only once or twice a year, so these tables are cheap to keep
current; a branch not listed falls back to 8.4. Set php-version explicitly
to override.
The debian base is also derived from mediawiki-version: buster for
REL1_43/REL1_44 (their Selenium tests need that image's older Chromium, which
newer Chromium aborts on for those branches' test URLs) and bookworm
otherwise. The phan and api-testing stages always use bookworm. Set
debian explicitly to override.
Available bases and versions are whatever the
Wikimedia Docker registry publishes,
so not every debian / php-version combination exists. For example, to pin an
older PHP, such as when testing an older MediaWiki branch:
- uses: femiwiki/quibble-action@dc8d9ec9d6c86ba9805a77736c68f974d250aa8f # v1.0.0
with:
debian: buster
php-version: '8.3'| Name | Default | Description |
|---|---|---|
mediawiki-version |
REL1_45 |
MediaWiki branch to test against, for example master or REL1_43. |
git-source |
github |
Where MediaWiki and the dependencies are cloned from: github (the official read-only mirrors, immune to Gerrit's CI rate limiting) or gerrit (gerrit.wikimedia.org). |
stage |
all |
Stage to run. Any Quibble stage, or phan / coverage. |
project-path |
. |
Path to the extension or skin under test, relative to the workspace. Set it when the action is checked out at the workspace root (so it can be used as uses: ./) and the project is in a subdirectory. See Testing from the same repository. |
dependencies |
(none) | Whitespace/comma separated dependency extensions/skins. Takes priority over the requires clause and phan config. See Defining dependencies. |
exclude-dependencies |
(none) | Space-separated list of dependency names to skip. |
cache-key |
true |
Mixed into every cache key; change it to bust the caches. |
upload-logs |
false |
Upload Quibble's logs as an artifact (opt-in, captured on failure too). Mind storage cost, retention, and that the artifact is downloadable by anyone who can view the run. |
log-artifact-name |
quibble-logs |
Name for the uploaded Quibble logs artifact. |
docker-registry |
docker-registry.wikimedia.org |
Registry that hosts the images. |
docker-org |
releng |
Registry organization. |
debian |
derived | Debian base for the Quibble image (bookworm, or buster for REL1_43/REL1_44 non-phan stages). See Docker images. |
php-version |
derived | PHP version for the images and the host. Branch minimum (for phan, the image that installs its deps; phan runs in mediawiki-phan-testrun). See Docker images. |
quibble-docker-image |
(derived) | Override; quibble-<debian>-php<version> when empty. |
coverage-docker-image |
quibble-coverage |
Override for the single pcov-based coverage image. |
phan-docker-image |
mediawiki-phan-testrun |
Override for the phan run image; mediawiki-phan-testrun when empty. |
| Name | Description |
|---|---|
coverage |
Path to the generated coverage directory ($RUNNER_TEMP/cover). |
An x86-64 (amd64) Linux runner with Docker available, for example
ubuntu-latest. The Wikimedia Quibble Docker images are published only for
linux/amd64, so ARM runners (such as ubuntu-24.04-arm) are not supported.