Files
mc-webui/scripts/release.sh
T
MarekWo 4c6857a00f feat: numbered releases (2.1.0) with git tags and GitHub releases
The app already knew exactly which build was running - a calendar version
of commit date plus short hash - but nothing gave a release a name users
could quote, and the repo had no tags at all.

Adds a VERSION file as the single source of truth for a SemVer release
number, read by app/version.py alongside the existing build string rather
than replacing it: the number is for people, the build is for pinning down
a deploy, and both ship in /api/version and the template context. The menu
shows the release first with the build underneath. #versionText still holds
the build string, because the remote-update poller compares it to detect
that the server came back on a new build.

Resolution order is unchanged (frozen file > git > fallback), and a frozen
file written before this change still yields a correct release number, so
an already-deployed server does not need re-freezing to stay sane. The
container has no git, hence COPY VERSION into the image - otherwise a plain
'docker compose build' reports 0.0.0.

scripts/release.sh cuts a release from main: it refuses a dirty tree, a
wrong branch, a malformed number or an existing tag, extracts the notes
from the matching whatsnew section, then tags, pushes and publishes via gh.

Numbering starts at 2.1.0 rather than 1.x: the v2 line has been in
production since March and 'v1' is the archived pre-migration branch, so
1.x would have been ambiguous. Sections in whatsnew before 2.1.0 keep
their date-only headings - they were never tagged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 19:20:57 +02:00

83 lines
2.4 KiB
Bash
Executable File

#!/bin/bash
# Tag the current release and publish it on GitHub.
#
# Run on `main`, after merging `dev`. Reads the release number from VERSION
# and the release notes from the matching docs/whatsnew.md section, so there
# is exactly one place to edit each of them.
#
# ./scripts/release.sh # tag + GitHub release
# ./scripts/release.sh --dry-run # show what would happen, change nothing
set -e
DRY_RUN=false
[ "$1" = "--dry-run" ] && DRY_RUN=true
cd "$(dirname "$0")/.."
info() { echo -e "\033[0;34m[*]\033[0m $1"; }
success() { echo -e "\033[0;32m[+]\033[0m $1"; }
error() { echo -e "\033[0;31m[!]\033[0m $1" >&2; }
VERSION=$(tr -d ' \t\r\n' < VERSION)
TAG="v${VERSION}"
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
error "VERSION must be MAJOR.MINOR.PATCH, got '${VERSION}'"
exit 1
fi
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ]; then
error "Releases are cut from main, but you are on '${BRANCH}'"
exit 1
fi
if [ -n "$(git status --porcelain)" ]; then
error "Working tree is dirty - commit or stash first"
exit 1
fi
if git rev-parse "$TAG" >/dev/null 2>&1; then
error "Tag ${TAG} already exists - bump VERSION before releasing"
exit 1
fi
# Release notes: the whatsnew.md section headed "## <version> - <date>",
# up to the next "## " heading
NOTES=$(awk -v ver="$VERSION" '
$0 ~ "^## " ver "( |$|—)" { found = 1; next }
found && /^## / { exit }
found { print }
' docs/whatsnew.md | sed -e 's/^---$//' | awk 'NF || printed { print; printed = 1 }')
if [ -z "$(echo "$NOTES" | tr -d '[:space:]')" ]; then
error "No release notes for ${VERSION} in docs/whatsnew.md"
error "Expected a section headed: ## ${VERSION} - YYYY-MM-DD"
exit 1
fi
info "Release ${TAG} on $(git rev-parse --short HEAD)"
echo "----------------------------------------"
echo "$NOTES"
echo "----------------------------------------"
if [ "$DRY_RUN" = true ]; then
info "Dry run - no tag created, nothing published"
exit 0
fi
git tag -a "$TAG" -m "mc-webui ${VERSION}"
success "Tagged ${TAG}"
git push origin "$TAG"
success "Pushed ${TAG}"
if command -v gh >/dev/null 2>&1; then
echo "$NOTES" | gh release create "$TAG" --title "mc-webui ${VERSION}" --notes-file -
success "Published GitHub release ${TAG}"
else
info "gh CLI not found - create the release manually at:"
info " https://github.com/MarekWo/mc-webui/releases/new?tag=${TAG}"
fi