Reusable GitHub Actions CI workflows for MediaWiki skins and extensions. Call them from your project's workflow file — no need to copy and maintain CI logic yourself.
Add a .github/workflows/ci.yml to your project:
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
lint:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/lint.yml@main
with:
lint-php: true
lint-js: true
test-js:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-js.yml@main
test-php:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-php.yml@main
with:
project-type: skin # or 'extension'
project-name: MySkin # your project's directory nameYour project needs:
- PHP:
composer.jsonwithtest(PHPCS),phan, and PHPUnit scripts - JS:
package.jsonwithlint:js,lint:styles,lint:i18n,lint:mdscripts and Vitest - Coverage (optional): Vitest outputs to
coverage/js/lcov.info; PHPUnit outputs Clover XML
Not every project needs all of these — only include the workflows and inputs relevant to your stack.
Runs PHP, JS, style, i18n, and markdown linters. Enable only the ones you need.
| Input | Type | Default | Description |
|---|---|---|---|
lint-php |
boolean | false |
Run composer test |
lint-js |
boolean | false |
Run npm run lint:js |
lint-styles |
boolean | false |
Run npm run lint:styles |
lint-i18n |
boolean | false |
Run npm run lint:i18n |
lint-md |
boolean | false |
Run npm run lint:md |
lint:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/lint.yml@main
with:
lint-php: true
lint-js: true
lint-styles: true
lint-i18n: true
lint-md: trueDownloads a MediaWiki branch, installs your project into it, and runs composer phan.
| Input | Type | Default | Description |
|---|---|---|---|
project-type |
string | required | skin or extension |
project-name |
string | required | Directory name (e.g. Citizen, TabberNeue) |
mw-branch |
string | REL1_43 |
MediaWiki branch |
php-version |
string | 8.2 |
PHP version |
extra-extensions |
string | "" |
Space-separated extra extensions for type resolution (e.g. Scribunto) |
skip-cache |
boolean | false |
Skip MW cache (for nightly runs) |
analyze-php:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/analyze-php.yml@main
with:
project-type: skin
project-name: CitizenRuns Vitest with coverage and caches the results for SonarQube.
No inputs required.
test-js:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-js.yml@mainRuns PHPUnit across a matrix of MediaWiki branches and PHP versions.
| Input | Type | Default | Description |
|---|---|---|---|
project-type |
string | required | skin or extension |
project-name |
string | required | Directory name (e.g. Citizen, TabberNeue) |
matrix |
string | (see below) | JSON array of matrix entries |
skip-cache |
boolean | false |
Skip MW cache (for nightly runs) |
Default matrix:
[
{"mw": "REL1_43", "php": "8.2", "coverage": "xdebug", "experimental": false},
{"mw": "REL1_44", "php": "8.3", "coverage": "none", "experimental": false},
{"mw": "REL1_45", "php": "8.4", "coverage": "none", "experimental": false},
{"mw": "master", "php": "8.5", "coverage": "none", "experimental": true}
]| Field | Description |
|---|---|
mw |
MediaWiki branch |
php |
PHP version |
coverage |
xdebug to generate coverage, none to skip |
experimental |
true allows failure without failing the workflow |
test-php:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-php.yml@main
with:
project-type: skin
project-name: CitizenTo customize the matrix:
test-php:
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-php.yml@main
with:
project-type: extension
project-name: TabberNeue
matrix: >
[
{"mw": "REL1_43", "php": "8.2", "coverage": "xdebug", "experimental": false},
{"mw": "REL1_44", "php": "8.3", "coverage": "none", "experimental": false}
]Runs a SonarQube scan with coverage data from test-js and test-php. Coverage is cached per branch, so SonarQube always has data for both JS and PHP even when only one test suite ran.
| Input | Type | Default | Description |
|---|---|---|---|
has-js-coverage |
boolean | false |
Whether JS coverage was generated this run |
has-php-coverage |
boolean | false |
Whether PHP coverage was generated this run |
enabled |
boolean | true |
Set false to skip |
Requires a SONAR_TOKEN secret (optional — the scan is skipped automatically if the token is unavailable, e.g. on fork PRs). Set it up at SonarCloud > Your project > Administration > Analysis Method.
Your project also needs a sonar-project.properties file. See SonarCloud docs.
sonarqube:
needs: [test-js, test-php]
if: always() && !failure()
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/sonarqube.yml@main
with:
has-js-coverage: ${{ needs.test-js.result == 'success' }}
has-php-coverage: ${{ needs.test-php.result == 'success' }}
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}A complete caller workflow with change detection, conditional jobs, and nightly cache refresh:
Show full example
name: CI
on:
schedule:
- cron: "0 0 * * *"
push:
branches: [main]
pull_request:
branches: ["**"]
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
php: ${{ steps.filter.outputs.php_any_changed }}
script: ${{ steps.filter.outputs.script_any_changed }}
stylesheet: ${{ steps.filter.outputs.stylesheet_any_changed }}
i18n: ${{ steps.filter.outputs.i18n_any_changed }}
markdown: ${{ steps.filter.outputs.markdown_any_changed }}
steps:
- uses: actions/checkout@v6
- uses: tj-actions/changed-files@v47
id: filter
with:
files_yaml: |
php:
- includes/**/*.php
- tests/**/*.php
- skin.json # or extension.json
- composer.json
- composer.lock
- .phan/config.php
- .github/workflows/ci.yml
script:
- resources/**/*.js
- tests/vitest/**
- vitest.config.js
- package.json
- package-lock.json
- .eslintrc.json
- .github/workflows/ci.yml
stylesheet:
- resources/**/*.css
- resources/**/*.less
- skinStyles/**/*.css
- skinStyles/**/*.less
- package.json
- package-lock.json
- .github/workflows/ci.yml
i18n:
- i18n/*.json
- package.json
- package-lock.json
- .github/workflows/ci.yml
markdown:
- "*.md"
- .markdownlint.json
- .github/workflows/ci.yml
lint:
needs: changes
if: >-
needs.changes.outputs.php == 'true' ||
needs.changes.outputs.script == 'true' ||
needs.changes.outputs.stylesheet == 'true' ||
needs.changes.outputs.i18n == 'true' ||
needs.changes.outputs.markdown == 'true' ||
github.event_name == 'schedule'
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/lint.yml@main
with:
lint-php: ${{ needs.changes.outputs.php == 'true' || github.event_name == 'schedule' }}
lint-js: ${{ needs.changes.outputs.script == 'true' || github.event_name == 'schedule' }}
lint-styles: ${{ needs.changes.outputs.stylesheet == 'true' || github.event_name == 'schedule' }}
lint-i18n: ${{ needs.changes.outputs.i18n == 'true' || github.event_name == 'schedule' }}
lint-md: ${{ needs.changes.outputs.markdown == 'true' || github.event_name == 'schedule' }}
analyze-php:
needs: changes
if: needs.changes.outputs.php == 'true' || github.event_name == 'schedule'
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/analyze-php.yml@main
with:
project-type: skin # or 'extension'
project-name: MySkin # your project's directory name
skip-cache: ${{ github.event_name == 'schedule' }}
test-js:
needs: changes
if: needs.changes.outputs.script == 'true' || github.event_name == 'schedule'
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-js.yml@main
test-php:
needs: changes
if: needs.changes.outputs.php == 'true' || github.event_name == 'schedule'
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/test-php.yml@main
with:
project-type: skin # or 'extension'
project-name: MySkin # your project's directory name
skip-cache: ${{ github.event_name == 'schedule' }}
sonarqube:
needs: [test-js, test-php]
if: always() && !failure()
uses: StarCitizenTools/mediawiki-ci-workflows/.github/workflows/sonarqube.yml@main
with:
has-js-coverage: ${{ needs.test-js.result == 'success' }}
has-php-coverage: ${{ needs.test-php.result == 'success' }}
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}| What | Cache key | Shared across | Refreshed by |
|---|---|---|---|
| MediaWiki installation | mw-<branch>-php<version> |
test-php and analyze-php |
skip-cache: true (nightly) |
| Composer packages | composer-php<version> |
All MW branches | Automatic (Composer) |
| JS coverage | coverage-js-<branch> |
SonarQube runs | Each test-js run |
| PHP coverage | coverage-php-<branch> |
SonarQube runs | Each test-php run |
Requires Go. Install all dev tools and enable the pre-commit hook:
make setupThis installs actionlint, yamlfmt, and lefthook, then configures lefthook to run them automatically before each commit.