Table of Contents
- Update Server Files
- Overview
- Stability Tags
- update-server.yml Trigger Behavior
- Cascade Release Channels
- Sync to Main
- How Joomla Filters Updates
- Dolibarr Stability
- Branch Lifecycle
- Dolibarr: update.txt
- Joomla: updates.xml (Multi-Entry)
- Platform Distribution
- Complete Multi-Entry Example
- Which Workflow Writes Which Entry
- XML Elements
- Extension Manifest Setup
- How It Works
- Health Checks
- Rulesets
- Update Server Priority
← Home
Update Server Files
moko-platform automatically generates platform-specific update server files on every release and dev/alpha/beta/rc deployment.
Overview
| Platform | File | Format | Reference |
|---|---|---|---|
Dolibarr (crm-module) |
update.txt |
Plain text version string (< 30 chars) | Dolibarr url_last_version check |
Joomla (waas-component) |
updates.xml |
Multi-entry XML following Joomla update server spec | Joomla Update Server Docs |
Stability Tags
Joomla's updates.xml contains multiple <update> entries simultaneously — one per stability level. Joomla filters which entries the admin sees based on the site's Minimum Stability setting (Extensions > Update > Options).
| Stability | Joomla <tag> |
Who sees it | Source workflow |
|---|---|---|---|
| Stable | <tag>stable</tag> |
All sites (default) | auto-release.yml on main |
| Release Candidate | <tag>rc</tag> |
Sites set to RC or lower | update-server.yml on rc/** push |
| Beta | <tag>beta</tag> |
Sites set to Beta or lower | update-server.yml on beta/** push |
| Alpha | <tag>alpha</tag> |
Sites set to Alpha or lower | update-server.yml on alpha/** push |
| Development | <tag>development</tag> |
Sites set to Development | update-server.yml on dev or dev/** push |
Note: Alpha and beta are optional stages. Not every release cycle will have alpha/beta entries.
update-server.yml Trigger Behavior
The update-server.yml workflow triggers on both direct pushes and PR merges to the following branches:
dev(bare branch — no sub-path required)dev/**(versioned dev branches likedev/02.01)alpha/**beta/**rc/**
Previously, the workflow only triggered on PR merges. The addition of push triggers ensures that direct commits to these branches (e.g., CI-generated version bumps, automated fixes) also update the updates.xml entries.
Cascade Release Channels
Each stability level writes its own channel and all lower channels to updates.xml. This ensures Joomla sites on any "Minimum Stability" setting always see the latest available release:
| Release Stream | Channels written to updates.xml |
|---|---|
| development | development |
| alpha | development, alpha |
| beta | development, alpha, beta |
| rc | development, alpha, beta, rc |
| stable | development, alpha, beta, rc, stable |
Without cascade, a site set to "Development" minimum stability would only see <tag>development</tag> entries and would miss stable releases entirely. The cascade ensures stable releases are visible to all sites regardless of their minimum stability setting.
For full cascade documentation, see Cascade Release Channels.
Sync to Main
Since Joomla sites read updates.xml from the main branch, the update-server.yml workflow syncs updates.xml to main via the Gitea API after building on non-main branches. This ensures pre-release channel entries (dev, alpha, beta, rc) are visible to sites checking for updates, without requiring a PR merge to main.
Previously, update-server.yml only committed updates.xml to the current branch, so Joomla sites never saw dev/alpha/beta/rc releases until they were merged to main.
How Joomla Filters Updates
Joomla's update system reads all <update> entries from the XML file but only presents entries whose <tag> matches the site's minimum stability threshold:
- Minimum Stability = Stable (default): Only sees
<tag>stable</tag>entries - Minimum Stability = RC: Sees
stable+rcentries - Minimum Stability = Beta: Sees
stable+rc+betaentries - Minimum Stability = Alpha: Sees
stable+rc+beta+alphaentries - Minimum Stability = Development: Sees all entries (
stable+rc+beta+alpha+development)
The admin always gets the highest version among visible entries.
Dolibarr Stability
| Workflow | Branch | update.txt content |
|---|---|---|
auto-release.yml |
main |
XX.YY.ZZ (real version) |
deploy-dev.yml |
rc/** |
XX.YY.ZZ-rc |
deploy-dev.yml |
beta/** |
XX.YY.ZZ-beta |
deploy-dev.yml |
alpha/** |
XX.YY.ZZ-alpha |
deploy-dev.yml |
dev/** |
development |
Branch Lifecycle
dev → [alpha] → [beta] → rc → version/XX → main → dev
optional optional (integration) (production) (feedback)
devordev/**: Active development. Update files tagged asdevelopment.alpha/**: (Optional) Early internal testing. Update files tagged asalpha. Can be skipped.beta/**: (Optional) Broader external testing. Update files tagged asbeta. Can be skipped.rc/**: Release candidate. Update files tagged asrc. RC branches deploy to dev server for final testing.version/XX: Major version integration branch (major only). All minors and patches flow into the same major branch.main: Stable release.auto-release.ymlcreates GitHub Release andvXXtag. Update files tagged asstable.- Main merges back to
devto start the next cycle.
Dolibarr: update.txt
Dolibarr modules check for updates by fetching a plain-text file from the URL in $this->url_last_version. The file must contain only the version string (e.g., 01.02.03) — no JSON, no XML, no newlines.
On release (main):
01.02.03
On RC deploy (rc/):**
01.02.03-rc
On dev deploy (dev/):**
development
The module descriptor's url_last_version should point to:
https://git.mokoconsulting.tech/MokoConsulting/{repo}/raw/branch/main/update.txt
Joomla: updates.xml (Multi-Entry)
The updates.xml file contains up to five stability entries at once (one per stability level). Joomla reads the entire file and filters by the site's minimum stability setting.
Platform Distribution
| Release Type | Gitea Release | GitHub Release | Download URLs |
|---|---|---|---|
| Stable | Yes | Yes (via mirror) | Dual (Gitea + GitHub) |
| RC | Yes | No | Single (Gitea only) |
| Beta | Yes | No | Single (Gitea only) |
| Alpha | Yes | No | Single (Gitea only) |
| Development | Yes | No | Single (Gitea only) |
Pre-release builds stay on Gitea for internal testing. Only stable releases are mirrored to GitHub.
Complete Multi-Entry Example
<?xml version="1.0" encoding="utf-8"?>
<updates>
<update>
<name>My Extension</name>
<description>My Extension stable release</description>
<element>com_myextension</element>
<type>component</type>
<version>01.02.03</version>
<tags>
<tag>stable</tag>
</tags>
<infourl title="My Extension">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/releases</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/releases/download/v01/com_myextension-01.02.03.zip</downloadurl>
<downloadurl type="full" format="zip">https://github.com/mokoconsulting-tech/MyExtension/releases/download/v01/com_myextension-01.02.03.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="((4\.[3-9])|(5\.[0-9]))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>My Extension</name>
<description>My Extension release candidate</description>
<element>com_myextension</element>
<type>component</type>
<version>01.03.01-rc</version>
<tags>
<tag>rc</tag>
</tags>
<infourl title="My Extension">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/src/branch/rc</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/releases/download/rc/com_myextension-01.03.01-rc.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="((4\.[3-9])|(5\.[0-9]))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>My Extension</name>
<description>My Extension beta build</description>
<element>com_myextension</element>
<type>component</type>
<version>01.03.01-beta</version>
<tags>
<tag>beta</tag>
</tags>
<infourl title="My Extension">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/src/branch/beta</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/releases/download/beta/com_myextension-01.03.01-beta.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="((4\.[3-9])|(5\.[0-9]))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>My Extension</name>
<description>My Extension alpha build</description>
<element>com_myextension</element>
<type>component</type>
<version>01.03.01-alpha</version>
<tags>
<tag>alpha</tag>
</tags>
<infourl title="My Extension">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/src/branch/alpha</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/releases/download/alpha/com_myextension-01.03.01-alpha.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="((4\.[3-9])|(5\.[0-9]))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>My Extension</name>
<description>My Extension development build</description>
<element>com_myextension</element>
<type>component</type>
<version>01.04.00-dev</version>
<tags>
<tag>development</tag>
</tags>
<infourl title="My Extension">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/src/branch/dev</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MyExtension/releases/download/development/com_myextension-01.04.00-dev.zip</downloadurl>
</downloads>
<targetplatform name="joomla" version="((4\.[3-9])|(5\.[0-9]))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
</updates>
Which Workflow Writes Which Entry
| Workflow | Trigger | Entry written |
|---|---|---|
auto-release.yml |
Push to main |
<tag>stable</tag> — writes the stable entry with SHA-256 hash of the ZIP |
update-server.yml |
Push to rc/** |
<tag>rc</tag> — adds/updates the RC entry (+ cascaded lower channels) |
update-server.yml |
Push to beta/** |
<tag>beta</tag> — adds/updates the beta entry (+ cascaded lower channels) |
update-server.yml |
Push to alpha/** |
<tag>alpha</tag> — adds/updates the alpha entry (+ cascaded lower channels) |
update-server.yml |
Push to dev or dev/** |
<tag>development</tag> — adds/updates the development entry |
The auto-release.yml workflow writes the stable entry and preserves any existing pre-release entries. The update-server.yml workflow writes its specific entry (and cascaded lower channels) and preserves the others.
Important: All update-server.yml runs also sync the updated updates.xml to main via the Gitea API, since Joomla sites read the update server XML from the main branch.
XML Elements
All metadata is auto-extracted from the extension's XML manifest at build time:
| Element | Source | Notes |
|---|---|---|
<name> |
<name> in manifest |
Extension display name |
<element> |
<element> in manifest, or manifest filename |
Must match installed extension |
<type> |
type attribute on <extension> |
component, module, plugin, library, package, template |
<client> |
client attribute on <extension> |
site or administrator — required for plugins and modules |
<folder> |
group attribute on <extension> |
Plugin group (e.g., system, content) — required for plugins |
<version> |
README.md VERSION field | Real version on release, development on dev |
<tags><tag> |
Workflow determines | stable on release, development on dev |
<targetplatform> |
<targetplatform> in manifest |
Falls back to Joomla 5.x / 6.x |
<php_minimum> |
<php_minimum> in manifest |
Optional, included if present |
<infourl> |
GitHub release/branch URL | Links to release page or branch |
<downloads><downloadurl> |
GitHub release asset or branch archive | type="full" format="zip" |
Extension Manifest Setup
For the updates.xml generation to work correctly, your Joomla extension manifest must include:
<extension type="component" client="site" method="upgrade">
<name>My Extension</name>
<element>com_myextension</element>
<updateservers>
<server type="extension" priority="1" name="My Extension Update Server (Gitea)">
https://git.mokoconsulting.tech/mokoconsulting-tech/{repo}/raw/branch/main/updates.xml
</server>
<server type="extension" priority="2" name="My Extension Update Server (GitHub)">
https://raw.githubusercontent.com/mokoconsulting-tech/{repo}/main/updates.xml
</server>
</updateservers>
</extension>
The <updateservers> tag tells Joomla where to check for updates. Both servers are declared for redundancy — Gitea (primary, priority 1) and GitHub mirror (fallback, priority 2).
How It Works
Joomla (updates.xml)
-
On release (
auto-release.yml→ main branch):- Builds ZIP from
src/directory - Uploads ZIP to the
vXXmajor release on GitHub - Computes SHA-256 hash of the ZIP
- Writes/updates the
<tag>stable</tag>entry inupdates.xmlwith version, download URL, and SHA-256 - Cascades to all 5 stability channels
- Preserves any existing rc/dev entries in the file
- Commits updated
updates.xmlto main
- Builds ZIP from
-
On RC push (
update-server.yml→ rc/** branches):- Writes/updates the
<tag>rc</tag>entry inupdates.xml - Cascades to
rc,beta,alpha, anddevelopmentchannels - Download URL points to the Gitea release ZIP
- Preserves all other stability entries
- Commits updated
updates.xmlto the rc branch - Syncs
updates.xmltomainvia Gitea API
- Writes/updates the
-
On beta push (
update-server.yml→ beta/** branches):- Writes/updates the
<tag>beta</tag>entry inupdates.xml - Cascades to
beta,alpha, anddevelopmentchannels - Download URL points to the Gitea release ZIP
- Preserves all other stability entries
- Commits updated
updates.xmlto the beta branch - Syncs
updates.xmltomainvia Gitea API
- Writes/updates the
-
On alpha push (
update-server.yml→ alpha/** branches):- Writes/updates the
<tag>alpha</tag>entry inupdates.xml - Cascades to
alphaanddevelopmentchannels - Download URL points to the Gitea release ZIP
- Preserves all other stability entries
- Commits updated
updates.xmlto the alpha branch - Syncs
updates.xmltomainvia Gitea API
- Writes/updates the
-
On dev push (
update-server.yml→devordev/**branches):- Writes/updates the
<tag>development</tag>entry inupdates.xml - Download URL points to the Gitea release ZIP
- Preserves all other stability entries
- Commits updated
updates.xmlto the dev branch - Syncs
updates.xmltomainvia Gitea API
- Writes/updates the
Dolibarr (update.txt)
- On release (
auto-release.yml→ main): writes real version toupdate.txt - On RC deploy (
deploy-dev.yml→ rc/**): writesXX.YY.ZZ-rctoupdate.txt - On beta deploy (
deploy-dev.yml→ beta/**): writesXX.YY.ZZ-betatoupdate.txt - On alpha deploy (
deploy-dev.yml→ alpha/**): writesXX.YY.ZZ-alphatoupdate.txt - On dev deploy (
deploy-dev.yml→ dev/**): writesdevelopmenttoupdate.txt
RC --> Main Flow
When a release candidate is ready:
rc/XX.YY.ZZbranch is tested with<tag>rc</tag>update entries- RC branch is merged to
mainvia PR - Push to main triggers
auto-release.yml→ GitHub Release +vXXtag updates.xmlon main gets a new/updated<tag>stable</tag>entryversion/XXarchive branch is auto-created
Health Checks
The platform-specific repo_health.yml workflows verify:
- Dolibarr:
update.txtexists in root, module descriptor valid, url_last_version correct - Joomla:
updates.xmlexists in root, XML manifest valid, language files present
Rulesets
Branch protection rulesets (applied via sync_rulesets.php):
- MAIN: prevents deletion, non-fast-forward, requires PRs
- VERSION: immutable — prevents updates, deletion, force-push
- DEV: prevents deletion, non-fast-forward
- ALPHA: prevents deletion, non-fast-forward
- BETA: prevents deletion, non-fast-forward
- RC: prevents deletion, non-fast-forward
Update Server Priority
Joomla manifest <updateservers> entries MUST follow this priority order:
| Priority | Server | URL pattern |
|---|---|---|
| 1 (primary) | Gitea | https://git.mokoconsulting.tech/MokoConsulting/{REPO}/raw/branch/main/updates.xml |
| 2 (fallback) | GitHub | https://raw.githubusercontent.com/mokoconsulting-tech/{REPO}/main/updates.xml |
Why Gitea first
- Gitea is the source of truth — all CI/CD runs on Gitea, releases are created here first
- GitHub is a push mirror — it may lag behind by minutes or hours
- Self-hosted control — Gitea is under our infrastructure, GitHub is third-party
- Availability — if GitHub has an outage, Joomla sites still get updates from Gitea
Manifest example
<updateservers>
<server type="extension" priority="1" name="ExtensionName Update Server (Gitea)">
https://git.mokoconsulting.tech/MokoConsulting/RepoName/raw/branch/main/updates.xml
</server>
<server type="extension" priority="2" name="ExtensionName Update Server (GitHub)">
https://raw.githubusercontent.com/mokoconsulting-tech/RepoName/main/updates.xml
</server>
</updateservers>
Enforcement
The enforce_tags.sh script and repo_health.yml workflow validate this ordering.
Repos with GitHub as priority 1 will be flagged as non-compliant.