Real, monotonic and reproducible plugin versioning (DEC-0068) - #111
Merged
Conversation
Supersedes the sentinel scheme of DEC-0030. main carried
$plugin->version = 9999999999 with the real version stamped at package time
from the runner's date, which had three failure modes: installing from a
checkout bricks the site (every real release is a lower number, so Moodle
refuses it as a downgrade with no in-product recovery — the Marketplace listing
links the repository, so this path is real); rebuilding the same tag on another
day produced a different version.php; and the installed number told support
nothing. A low sentinel would be just as wrong in the other direction:
$plugin->version takes part in the install/upgrade protocol and must stay above
every savepoint in db/upgrade.php — which is also why the 2026070701 proposed
in the earlier fix was invalid against the existing 2026072400 savepoint.
version.php now commits a real version: 2026072401, the minimum valid
development value (strictly greater than the highest savepoint), with the
development marker where it belongs — $plugin->release = 'dev'. The version is
bumped whenever Moodle must detect a change (db/, classes/, JS, settings,
strings, tasks, capabilities, external services, cache-sensitive metadata).
Packaging no longer computes or rewrites anything: scripts/package.sh drops the
date-derived version and the sed rewrite of version.php in the temporary index,
so the ZIP ships version.php byte-identical to the committed file
(check-package.sh asserts it). Everything else about the temporary-index
packaging (bundled editor, packaged thirdpartylibs.xml, translation-marker
stripping, .distignore, git archive) is unchanged, and the working tree is
never touched.
scripts/check-version.sh (with a CI self-test) enforces the policy: sentinels
rejected in both directions, ten-digit plausible YYYYMMDDXX, savepoint bounds
(strict in development, >= for releases), release/tag agreement, and no 'dev'
metadata on tagged commits. make package now depends on the release validation,
and make check-version validates the committed state.
The release flow is: release-preparation PR commits the final version + semver
release -> merge -> tag that exact commit -> release.yml verifies the checkout
is the tagged commit and validates (never mutates) the metadata, builds the
editor and packages -> follow-up PR returns main to 'dev' with the next valid
version. The daily editor watcher no longer creates automated releases or
pushes to main; it opens an editor-pin sync PR instead. Documented in
DEVELOPMENT.md ("Versioning and releases"), the release checklist (section
10b) and ADR DEC-0068; DEC-0030 is marked superseded with its original context
preserved.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #111 +/- ##
=========================================
Coverage 92.31% 92.31%
Complexity 824 824
=========================================
Files 51 51
Lines 3734 3734
=========================================
Hits 3447 3447
Misses 287 287
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The policy (ADR DEC-0068, included; supersedes DEC-0030 with its context preserved)
mainalways carries a real, monotonic Moodle version — no sentinels in either direction (9999999999bricks upgrades;0/1/99999sit below existing savepoints) — with the development marker where it belongs:$plugin->release = 'dev'. The version is bumped whenever Moodle must detect a change:db/,classes/, JavaScript source or builds, settings, language strings, scheduled tasks, capabilities, external services, or other cache-sensitive metadata; always strictly above the latest published version and everyupgrade_mod_savepoint().Packaging: the date dependency is removed
scripts/package.shno longer computesYYYYMMDDfrom the runner date and no longer rewrites$plugin->version/$plugin->releasewithsedin the temporary index. The ZIP shipsversion.phpbyte-identical to the committed file — rebuilding the same tag on any day produces the sameversion.php. The rest of the temporary-index machinery is untouched: bundled editor underdist/static/, package-onlythirdpartylibs.xml, translation-marker stripping,.distignore,git archive, working tree never modified.scripts/check-package.shnow asserts the byte-identity.Release flow: metadata is committed before tagging; workflows validate, never mutate
$plugin->version = 2026072500; $plugin->release = '4.0.3';— the actual values are chosen when preparing the release).version.phpis never modified after the tag exists.release.ymlnow verifies the checked-out commit is the tagged commit, runscheck-version.sh --release, fails ondevmetadata, builds the editor, packages without rewriting anything, uploads. It never commits or pushes, and derives nothing from runner/workflow/release/tag dates.mainto'dev'with the next valid development version.The daily editor watcher (
check-editor-releases.yml) no longer creates automated plugin releases or pushes tomain— both incompatible with this policy. It now opens an editor-pin sync PR (.editor-version+ blueprint) and leaves the release decision to a maintainer. Manualworkflow_dispatchbuilds ofrelease.ymlproduce adev-labelled test artifact validated under the development rules.Validation
New
scripts/check-version.sh(Bash 3.2+/Ubuntu/Git Bash), run by CI (release-workflow-check),make check-version,make check-release-version RELEASE=X.Y.Z, and as a dependency ofmake package:9999999999,99999,0,1, …);YYYYMMDDXXdate structure;$oldversion <guard fromdb/upgrade.phpand enforces the bound (strict in development,>=for releases);$plugin->releaseto equal the expected value (never'dev') and, on an exactly-tagged commit, the tag without its leadingvto match;'dev'metadata on tagged commits in any mode;scripts/check-version-selftest.sh(also in CI) covers the required matrix: valid2026072401/dev; each sentinel; malformed versions; version equal to and lower than the highest savepoint; version above it; release/tag match and mismatch; release still carryingdev; a release-preparation commit validating without arguments.Test results
bash -nclean on all four scripts; self-test 17/17;make check-versionOK;scripts/check-package.shOK (working tree untouched, packagedversion.phpbyte-identical);scripts/check-release-workflow.shstill green on the newrelease.yml;phpcs --standard=moodle0/0 onversion.php.2026072500/ test release) was packaged andunzip -p … exelearning/version.phpdiffed against the committed file — identical; the commit was then reverted.Documentation
DEVELOPMENT.mdgains a "Versioning and releases" section (why no sentinels, when to bump, the five-step flow, tag immutability, validate-don't-mutate); the release checklist gains section 10b;version.phpitself documents the bump rules where contributors will see them.Moodle Playground Preview
The changes in this pull request can be previewed and tested using a Moodle Playground instance.
ℹ️ The eXeLearning editor is fetched from the shared release and unpacked into the plugin when the playground boots, so the first load may take a few extra seconds. ELPX upload, viewer and preview work normally.