Development
This page explains how Wikven is built, for people who want to work on it. Wikven is a MediaWiki extension plus a handful of maintenance scripts that turn a directory of wikitext into a static website; it does not add a new rendering engine, it drives MediaWiki itself and then captures the result.
Architecture
A Wikven build has four moving parts:
- The extension (
extension.json,includes/). It registers Wikven's configuration and a few hooks that adapt MediaWiki's output for a static host: hiding the edit/history UI and the sidebar's wiki-only links, swapping footer links for the configuredWikvenFooterUrl/WikvenEditUrl/WikvenHistoryUrl, and rewriting internal URLs so they point at.htmlfiles instead ofindex.php?title=. - The maintenance scripts (
maintenance/). These are the build pipeline, described below. WikvenSettings.php. TheLocalSettings.phpthe build runs under: it loads the configuration, detects the image backend, and applies Wikven's defaults.- The
bin/entrypointscript andDockerfile. They package MediaWiki 1.45 plus the extension into an image whose default command runs one build.
Building the image
docker build --tag wikven .
The Dockerfile is FROM mediawiki:1.45. It adds Composer and unzip (needed when fetching third-party extensions), copies the extension into /var/www/html/extensions/Wikven, copies WikvenSettings.php into the MediaWiki root, and sets the bin/entrypoint script as the image's default command (CMD).
Running a build
docker run --rm \
-v "$(pwd)/docs:/workspace/src" \
-v "$(pwd)/dist:/workspace/dist" \
wikven
The container mounts the source at /workspace/src and writes the rendered site to /workspace/dist. Both paths derive from WIKVEN_WORKDIR (default /workspace), which also has a .cache/ for ephemeral state; the same layout lets the standalone binary point everything at a writable host directory.
The run script:
- installs a throwaway MediaWiki into an SQLite database (no web server; the admin account it creates is never used),
- fetches any third-party extensions and skins (see below),
- appends
require_once 'WikvenSettings.php';to the generatedLocalSettings.php, - runs the build pipeline,
- deletes the per-page history the file cache emits.
The build pipeline
maintenance/build.php boots MediaWiki once and runs every step as a child maintenance script, rather than paying the bootstrap cost once per step. In order:
setMainPage- Points
MediaWiki:Mainpageat the importedindexarticle, so the site root resolves toindex.html. importImages- Uploads the image files in the source directory into the
File:namespace. It runs before the wikitext import, so pages that embed a local image render a real thumbnail instead of a broken-media placeholder. importWikitext- Imports every
*.wikitextfile (recursing into subdirectories, so a page likeTemplate:Foo/styles.csscan be a nested file).File:description pages andMediaWiki:system pages are saved as the current revision so their edit hooks fire; ordinary pages are imported as old revisions stamped with the file's modification time, so the footer shows a real "last modified" date. RunJobs- Runs MediaWiki's job queue, mainly thumbnail generation and link-table updates.
RebuildFileCache- Renders every content page to an HTML file under
dist/. This is MediaWiki's own file cache; Wikven treatsFile:as a content namespace too, so file description pages are exported without dumping every template. buildStyles- Re-renders each CSS module the pages reference into a local file, and renders the
site.stylesmodule (MediaWiki:Common.cssand the skin's site CSS) to its own file. buildScripts- Dumps the JavaScript the pages need into two files:
startup-static.js(the loader manifest, with its network auto-load removed) andmodules-static.js(the full dependency closure, so every module self-executes). It seeds thesitemodule (MediaWiki:Common.jsand the skin's JS) and any default gadgets, which a live wiki pulls in implicitly. rewriteScripts- Rewrites the cached HTML to load the local bundle instead of
load.php: it swaps the async startup tag for the static files plus an explicitmw.loader.load(), links the site styles last so they win the cascade, and removes the appearance menu (and the search box, unless SifterSearch is providing a static index), which need a live API. storeImages- Copies every referenced image (from Wikimedia Commons via InstantCommons, or from local uploads) to a content-hashed local file and rewrites the URLs, so nothing is fetched over the network at view time.
rename- Gives the namespace-prefixed cache files readable names (for example
ns6%3A...becomesFile:...).
Configuration
Configuration is layered. default.yml ships Wikven's baseline MediaWiki settings; the site's .wikven.yaml is merged on top. WikvenSettings.php reads both with MediaWiki's own YAML settings parser, applies the merged config map, loads the listed extensions and skins, and points $wgLogos at the uploaded WikvenLogos files. It also detects the thumbnailing backend at run time, ImageMagick and rsvg when available, otherwise the built-in GD library plus native inline SVG, so the same build works in the image and in a binary that has only GD.
Why these steps exist
A live wiki relies on load.php, an action API, and a database; a static host has none of these. Each pipeline step removes one such dependency: the file cache replaces the renderer, buildStyles/buildScripts/rewriteScripts replace load.php, storeImages replaces the thumbnail/Commons endpoints, and the extension's hooks remove the UI that would call the API. What is left is plain HTML, CSS, JS, and images.
Third-party extensions and skins
Names in extensions/skins that are not bundled with Wikven are fetched at build time by maintenance/fetchExtensions.php, from a source declared in WikvenRepositories (a tarball, a Git repository, or a Composer package). This runs after install but before WikvenSettings.php is wired in, so the components are on disk by the time MediaWiki loads them.
Standalone binary
The same MediaWiki + Wikven tree is embedded into a single FrankenPHP executable (see binary.Dockerfile), so a site can be built with no Docker. The binary extracts its app to a writable temporary directory at startup and runs the same pipeline, which is why all writable paths are kept under WIKVEN_WORKDIR.
Continuous integration
lint.ymlruns the formatters and linters (Biome, mago, rumdl, taplo, typos, yamllint, zizmor, and phpcs).docker-build.ymlbuilds the image and publishes it toghcr.io/chaotic-ground/wikvenonmain.deploy-docs.ymlbuilds thedocs/directory with the image and deploys the result to GitHub Pages, so this site is itself a Wikven build.build-binary.ymlis a reusable workflow that builds the standalone binary for each platform.release-please.ymlcalls it to attach the binaries to a version release, andnightly.ymlcalls it daily to publish a datednightly-YYYY-MM-DDpre-release. Both create the release as a draft, attach the binaries, then publish it, because GitHub's immutable releases lock a release's assets once it is published.release-please.ymlmanages versioning and releases (see below).
Versions follow Conventional Commits, which release-please uses to generate the changelog and bump the version.