Release Automation
Libre WebUI releases are created from the repository root with the release script. The script reads real git history since the previous version tag, updates package versions, writes the changelog, runs release checks, commits the release, and creates the version tag. GitHub is the build and binary publication source; release metadata and named artifact links are mirrored to the project's Forgejo repository.
One-Time Local Setup
Install dependencies and enable the repository hooks:
npm install
npm run setup-hooks
The hook setup configures:
.githooks/commit-msgfor Conventional Commit validation.githooks/pre-commitfor formatting checks.gitmessageas the local commit message template
Create a Release
Run the release script from a clean worktree on the branch you intend to tag:
# Patch release
npm run release
# Minor release
npm run release:minor
# Major release
npm run release:major
The script automatically:
- Checks that the working tree is clean and the next local tag is available.
- Collects commit, file, dependency, locale, and unreleased changelog evidence.
- Generates the release notes from that evidence.
- Updates
package.json, workspace package files,package-lock.json, the Helm chart and app versions, andCHANGELOG.md. - Runs
npm run release:check, including formatting, lint, builds, tests, security audit, and the npm publish dry-run. - Only after every check passes, commits the release and creates the annotated version tag.
Changelog Generation
Preview the next changelog section without changing files:
npm run changelog
Update CHANGELOG.md manually from the generated section:
npm run changelog -- update
By default, changelog generation can ask a local Ollama-compatible model for a polished draft, then validates the result against the collected git evidence. If AI is unavailable or the output looks unsafe, the script falls back to a deterministic generator.
Useful overrides:
CHANGELOG_AI=0 npm run release:minor
CHANGELOG_AI_MODEL=glm-5.2:cloud npm run changelog
OLLAMA_BASE_URL=http://127.0.0.1:11434 npm run release
Push a Release
After the release commit and annotated tag are created, publish only the exact branch commit and tag shown by the script. The production branch is pushed to Forgejo first, then GitHub, with followed tags disabled explicitly:
git -c push.followTags=false push \
https://git.kroonen.ai/libre-webui/libre-webui.git \
HEAD:refs/heads/main
git ls-remote \
https://git.kroonen.ai/libre-webui/libre-webui.git \
refs/heads/main
git -c push.followTags=false push \
https://github.com/libre-webui/libre-webui.git \
HEAD:refs/heads/main
git ls-remote \
https://github.com/libre-webui/libre-webui.git \
refs/heads/main
Both returned branch SHAs must equal the intended local release commit. Wait for the required GitHub workflows for that exact commit to pass before publishing the tag.
Confirm the version tag does not already exist on either service, then push that one tag to Forgejo first and GitHub second:
git ls-remote \
https://git.kroonen.ai/libre-webui/libre-webui.git \
'refs/tags/vX.Y.Z' 'refs/tags/vX.Y.Z^{}'
git ls-remote \
https://github.com/libre-webui/libre-webui.git \
'refs/tags/vX.Y.Z' 'refs/tags/vX.Y.Z^{}'
git -c push.followTags=false push \
https://git.kroonen.ai/libre-webui/libre-webui.git \
refs/tags/vX.Y.Z:refs/tags/vX.Y.Z
git -c push.followTags=false push \
https://github.com/libre-webui/libre-webui.git \
refs/tags/vX.Y.Z:refs/tags/vX.Y.Z
git ls-remote \
https://git.kroonen.ai/libre-webui/libre-webui.git \
'refs/tags/vX.Y.Z' 'refs/tags/vX.Y.Z^{}'
git ls-remote \
https://github.com/libre-webui/libre-webui.git \
'refs/tags/vX.Y.Z' 'refs/tags/vX.Y.Z^{}'
Replace vX.Y.Z with the release tag. For an annotated tag, verify both the tag
object SHA and its peeled commit SHA. Never use git push --tags, which can
publish unrelated local tags.
CI Release Path
Pushing a v* tag runs the GitHub release workflow. The workflow:
- Runs
npm run release:check - Builds Electron artifacts for macOS, Windows, and Linux
- Creates the GitHub release from the matching
CHANGELOG.mdsection - Mirrors the release record and named artifact links to Forgejo
- Builds Docker images
- Publishes the Helm chart with the same version as the release tag
- Publishes the npm package with
NPM_TOKEN
The same check can be run locally before tagging:
npm run release:check
Forgejo Release Mirror
The mirror uses a Forgejo personal access token stored as the encrypted GitHub
Actions secret FORGEJO_TOKEN. Give the token only the
write:repository scope, ensure its owner can write to
libre-webui/libre-webui, and never commit or print the token.
The mirror is deliberately idempotent. It looks up releases by tag, creates only missing release records, reconciles their GitHub release metadata, and skips artifact links that already exist. A retry after a network or workflow failure therefore completes the missing work without duplicating releases or assets.
Forgejo release assets are named external links to the corresponding public
GitHub browser_download_url. GitHub remains the binary host, while Forgejo
shows the same downloadable filenames without duplicating tens of gigabytes of
desktop artifacts. Source archives remain generated independently from the
exact tag on each service.
Preview or Backfill One Release
Inspect what would change without writing to Forgejo:
node scripts/mirror-forgejo-releases.mjs --tag vX.Y.Z --dry-run
After loading FORGEJO_TOKEN and GITHUB_TOKEN into the process environment
from the maintainer's secret manager, mirror that release:
node scripts/mirror-forgejo-releases.mjs --tag vX.Y.Z
The exact tag must already exist on GitHub and Forgejo and resolve to the same tag object and peeled commit before a release is mirrored.
Preview or Backfill All Releases
Audit every GitHub Release against Forgejo:
node scripts/mirror-forgejo-releases.mjs --all --dry-run
Backfill every missing or incomplete Forgejo Release:
node scripts/mirror-forgejo-releases.mjs --all
GITHUB_TOKEN is required for --all, including dry runs, because exact tag
parity and asset discovery require more requests than GitHub's anonymous API
limit permits. FORGEJO_TOKEN is additionally required whenever --dry-run is
not used.
The --all path paginates both APIs and considers GitHub Release objects, not
every Git tag. A tag that intentionally has no GitHub Release remains tag-only
on Forgejo. Run the dry-run again after a backfill; it should report no pending
changes.
Immutable Tag Policy
Published version tags are immutable. After a tag exists on either remote:
- Do not delete it.
- Do not force-push it.
- Do not move it to a corrected commit.
- Do not reuse its semantic version for different contents.
If published release contents are wrong, correct the source and changelog and publish the next patch version. If only a release page or external asset link is missing, rerun the idempotent mirror without touching the tag.
The Forgejo v0.8.6 tag had a one-time, explicitly approved realignment during
the introduction of dual release mirroring. It repaired two historical tag
objects that described identical source trees but followed different commit
lineages. That audited migration is not a precedent for moving published tags.
Helm Version Policy
The Helm chart version, chart appVersion, root package version, and release
tag intentionally use the same semantic version. The release script advances
them together, and CI rejects a mismatch.
The chart is published only from an immutable v* release tag. Do not publish
modified chart contents under an existing chart version. A chart change must go
through the next application release so it receives a new version.
Chart version 0.14.1 carries a one-time digest override because that release
predates semantic Docker tags. The digest identifies the verified
multi-architecture 0.14.1 image. The release script clears this override when it
creates the next release, after which the default image resolves to the chart
appVersion.
The Docker workflow publishes that semantic-version tag to GHCR and Docker Hub
from the same v* release tag. Helm publication waits up to 20 minutes for the
matching public Docker Hub image and fails instead of publishing a chart with a
missing default image. The bundled Ollama image remains independently
configurable and defaults to its upstream latest tag.
Conventional Commits
Commit messages should use Conventional Commit format:
<type>[optional scope]: <description>
Common types:
feat: user-facing featurefix: bug fixdocs: documentation updaterefactor: internal code restructuringperf: performance improvementtest: test coveragechore: maintenance, release, or build work
Breaking changes use !:
git commit -m "feat!: remove deprecated endpoint"
git commit -m "fix(auth)!: change token validation"
Troubleshooting
Working Directory Is Not Clean
Commit or stash the local changes before releasing:
git status --short
git add .
git commit -m "fix: resolve pending changes"
No Releasable Changes
Check the commits since the previous tag:
git log $(git describe --tags --abbrev=0)..HEAD --oneline
Changelog Needs Manual Editing
Edit CHANGELOG.md, then commit the correction before publishing the tag:
git add CHANGELOG.md
git commit -m "docs: refine changelog"
Roll Back a Local Release Commit
If neither the release commit nor tag has been pushed:
git tag -d v0.12.0
git reset --soft HEAD~1
If either remote already has the tag, do not delete or replace it. Fix the
problem on main, create the next patch release, and publish that new immutable
tag through the complete gate.
Forgejo Mirror Is Incomplete
First verify that both remote tag object and peeled commit SHAs match. Then preview and retry the affected release:
node scripts/mirror-forgejo-releases.mjs --tag vX.Y.Z --dry-run
node scripts/mirror-forgejo-releases.mjs --tag vX.Y.Z
An authorization failure means FORGEJO_TOKEN is missing, expired, owned by a
user without repository access, or lacks write:repository. A missing or
different remote tag must be investigated separately; the release mirror never
creates or moves Git tags.
Maintainer Files
.gitmessage- commit message template.githooks/commit-msg- Conventional Commit validation.githooks/pre-commit- formatting preflightscripts/release.js- release orchestrationscripts/mirror-forgejo-releases.mjs- idempotent Forgejo release mirror and backfillscripts/generate-changelog.js- changelog preview/update commandscripts/lib/releaseNotes.js- evidence collection and changelog generation.github/workflows/release.yml- tag-driven CI release workflow.github/workflows/helm-publish.yml- Helm validation and tag publication
For more information about Conventional Commits, visit https://www.conventionalcommits.org/.