Jump to content

Development

From Wikven

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 configured WikvenFooterUrl/WikvenEditUrl/WikvenHistoryUrl, and rewriting internal URLs so they point at .html files instead of index.php?title=.
  • The maintenance scripts (maintenance/). These are the build pipeline, described below.
  • WikvenSettings.php. The LocalSettings.php the build runs under: it loads the configuration, detects the image backend, and applies Wikven's defaults.
  • The bin/entrypoint script and Dockerfile. 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:

  1. installs a throwaway MediaWiki into an SQLite database (no web server; the admin account it creates is never used),
  2. fetches any third-party extensions and skins (see below),
  3. appends require_once 'WikvenSettings.php'; to the generated LocalSettings.php,
  4. runs the build pipeline,
  5. 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:Mainpage at the imported index article, so the site root resolves to index.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 *.wikitext file (recursing into subdirectories, so a page like Template:Foo/styles.css can be a nested file). File: description pages and MediaWiki: 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 treats File: 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.styles module (MediaWiki:Common.css and 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) and modules-static.js (the full dependency closure, so every module self-executes). It seeds the site module (MediaWiki:Common.js and 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 explicit mw.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... becomes File:...).

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.yml runs the formatters and linters (Biome, mago, rumdl, taplo, typos, yamllint, zizmor, and phpcs).
  • docker-build.yml builds the image and publishes it to ghcr.io/chaotic-ground/wikven on main.
  • deploy-docs.yml builds the docs/ directory with the image and deploys the result to GitHub Pages, so this site is itself a Wikven build.
  • build-binary.yml is a reusable workflow that builds the standalone binary for each platform. release-please.yml calls it to attach the binaries to a version release, and nightly.yml calls it daily to publish a dated nightly-YYYY-MM-DD pre-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.yml manages versioning and releases (see below).

Versions follow Conventional Commits, which release-please uses to generate the changelog and bump the version.

Retrieved from "Development.html"