auto versioning CI releases

This commit is contained in:
Alex Vanderveen
2026-08-11 22:05:20 -04:00
parent 48a2a4dd7a
commit 2207cd9a09
4 changed files with 173 additions and 25 deletions
+7 -1
View File
@@ -30,6 +30,12 @@ cp psxrecomp/docs/ci/templates/setup-release.yml .github/workflows/release.yml
Template: [`templates/setup-release.yml`](templates/setup-release.yml)
**Versioning:** `workflow_dispatch` with an empty `version` auto-bumps the next
`X.Y.Z` from the latest `v*.*.*` git tag (`bump`: patch / minor / major; default
patch). Pass an explicit version to override. Shared helper:
`tools/ci/normalize_version.sh --next [patch|minor|major]`.
**Host-only model / player updates:** [`HOST_ONLY_RELEASES.md`](HOST_ONLY_RELEASES.md)
(CI never ships game C; RetComM Update reuses codegen-cache for host/UI bumps;
`reuse_cached_emitters` + ccache speed up release jobs when the `psxrecomp` pin
@@ -47,7 +53,7 @@ the ICD dynamically via SDL; CI only needs headers and the shader compiler.
| Script | Role |
|--------|------|
| `ci/normalize_version.sh` | Normalize / write `VERSION` + `TAG` |
| `ci/normalize_version.sh` | Normalize / write `VERSION` + `TAG`; `--next` auto-bumps from latest tag |
| `ci/clear_generated.sh` | Clear `generated/` for setup-host CI |
| `ci/record_pins.sh` | Log `psxrecomp` / `recomp-ui` / `recomp-net` SHAs (CI + scaffold) |
| `ci/verify_pins.sh` | Optional local check vs `framework_pins.txt` (not used by release CI) |
+59 -10
View File
@@ -20,6 +20,7 @@
# Release hosts always build with PSX_HAVE_VULKAN (headers + glslc); runtime
# still loads the ICD dynamically via SDL.
#
# workflow_dispatch: leave version empty to auto-bump patch (or choose bump).
# Host-focused releases: CI never ships game C. Players Generate once; RetComM
# Update reuses codegen-cache when ROM/BIOS/emitters match (cmake-only).
# See docs/ci/HOST_ONLY_RELEASES.md. reuse_cached_emitters skips rebuilding
@@ -31,9 +32,21 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g. 0.1.0 or v0.1.0)'
required: true
description: >-
Release version (e.g. 0.1.0 or v0.1.0). Leave empty to auto-bump
from the latest vX.Y.Z git tag (see bump).
required: false
type: string
default: ''
bump:
description: 'When version is empty, which semver component to increment'
required: false
type: choice
options:
- patch
- minor
- major
default: patch
publish:
description: 'Create git tag vX.Y.Z and GitHub Release'
required: true
@@ -68,6 +81,12 @@ jobs:
publish: ${{ steps.meta.outputs.publish }}
reuse_emitters: ${{ steps.meta.outputs.reuse_emitters }}
steps:
- name: Checkout (tags for auto-bump)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Normalize version / tag
id: meta
shell: bash
@@ -75,30 +94,60 @@ jobs:
set -euo pipefail
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
RAW="${{ github.event.inputs.version }}"
BUMP="${{ github.event.inputs.bump || 'patch' }}"
PUBLISH="${{ github.event.inputs.publish }}"
REUSE="${{ github.event.inputs.reuse_cached_emitters }}"
else
RAW="${{ github.ref_name }}"
BUMP="patch"
PUBLISH="true"
REUSE="true"
fi
RAW="$(printf '%s' "${RAW}" | tr -d '[:space:]')"
if [[ -z "${RAW}" ]]; then
echo "::error::version is empty"
# Prefer shared helper when present (optional); else inline bump.
if [[ -x psxrecomp/tools/ci/normalize_version.sh ]]; then
eval "$(bash psxrecomp/tools/ci/normalize_version.sh --next "${BUMP}" | grep -E '^(VERSION|TAG)=')"
VER="${VERSION}"
TAG="${TAG}"
else
LATEST="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -1 || true)"
BASE="${LATEST#v}"
if [[ -z "${BASE}" && -f VERSION ]]; then
BASE="$(tr -d '[:space:]' <VERSION)"
BASE="${BASE#v}"
fi
BASE="${BASE:-0.0.0}"
BASE="${BASE%%[-+]*}"
IFS=. read -r MA MI PA <<<"${BASE}"
MA="${MA:-0}"; MI="${MI:-0}"; PA="${PA:-0}"
case "${BUMP}" in
major) MA=$((MA + 1)); MI=0; PA=0 ;;
minor) MI=$((MI + 1)); PA=0 ;;
*) PA=$((PA + 1)) ;;
esac
VER="${MA}.${MI}.${PA}"
TAG="v${VER}"
echo "Auto-bumped ${BUMP}: ${LATEST:-none} → ${VER}"
fi
else
VER="${RAW#v}"
if [[ ! "${VER}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][A-Za-z0-9.+-]*)?$ ]]; then
echo "::error::invalid version '${RAW}' (want X.Y.Z or vX.Y.Z)"
exit 1
fi
TAG="v${VER}"
fi
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && \
git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "::error::tag ${TAG} already exists — bump again or pass an explicit version"
exit 1
fi
VER="${RAW#v}"
if [[ ! "${VER}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][A-Za-z0-9.+-]*)?$ ]]; then
echo "::error::invalid version '${RAW}' (want X.Y.Z or vX.Y.Z)"
exit 1
fi
TAG="v${VER}"
echo "version=${VER}" >>"$GITHUB_OUTPUT"
echo "tag=${TAG}" >>"$GITHUB_OUTPUT"
echo "publish=${PUBLISH}" >>"$GITHUB_OUTPUT"
echo "reuse_emitters=${REUSE}" >>"$GITHUB_OUTPUT"
echo "Using version ${VER} (tag ${TAG}), publish=${PUBLISH}, reuse_emitters=${REUSE}"
build:
name: ${{ matrix.artifact }}
needs: prepare
+10 -2
View File
@@ -30,15 +30,23 @@ endif()
# SOURCE + compiler + flags (content, not mtime), so those recompiles collapse to
# near-instant cache hits after any branch op. Completely no-op when ccache is not
# on PATH, so builds still work without it. Set once, before any target is added.
# RetComM cmake-clang-v1 packs ship bin/ccache and prepend that dir to PATH;
# also HINT RETCOMM_TOOLCHAIN_DIR for wizards that only set the env override.
if(NOT DEFINED CMAKE_C_COMPILER_LAUNCHER)
find_program(CCACHE_PROGRAM ccache)
set(_psx_ccache_hints "")
if(DEFINED ENV{RETCOMM_TOOLCHAIN_DIR} AND NOT "$ENV{RETCOMM_TOOLCHAIN_DIR}" STREQUAL "")
list(APPEND _psx_ccache_hints "$ENV{RETCOMM_TOOLCHAIN_DIR}/bin")
endif()
find_program(CCACHE_PROGRAM NAMES ccache ccache.exe HINTS ${_psx_ccache_hints})
unset(_psx_ccache_hints)
if(CCACHE_PROGRAM)
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE STRING "compiler launcher")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" CACHE STRING "compiler launcher")
message(STATUS "psxrecomp: ccache enabled (${CCACHE_PROGRAM}) — mtime-proof rebuilds")
else()
message(STATUS "psxrecomp: ccache not found; generated-C rebuilds after git "
"branch ops will be slow. Install ccache on PATH to fix.")
"branch ops will be slow. Install ccache on PATH (or update "
"cmake-clang-v1) to fix.")
endif()
endif()
+97 -12
View File
@@ -1,21 +1,43 @@
#!/usr/bin/env bash
# Normalize a release version string to X.Y.Z (strip leading v / whitespace).
# Normalize a release version string to X.Y.Z (strip leading v / whitespace),
# or compute the next semver from git tags / VERSION.
#
# Usage:
# normalize_version.sh <raw> # prints VERSION=… TAG=…
# normalize_version.sh --write VERSION <raw> # also writes VERSION file
# normalize_version.sh <raw> # prints VERSION=… TAG=…
# normalize_version.sh --write VERSION <raw> # also writes VERSION file
# normalize_version.sh --next [patch|minor|major] # bump from latest vX.Y.Z tag
# normalize_version.sh --write VERSION --next patch
#
# --next looks at (in order):
# 1) latest git tag matching vMAJOR.MINOR.PATCH*
# 2) VERSION file in cwd (if present)
# 3) 0.0.0
# then increments the chosen component (default: patch).
#
# When GITHUB_OUTPUT is set, appends version= and tag=.
set -euo pipefail
WRITE=""
RAW=""
NEXT=""
while [[ $# -gt 0 ]]; do
case "$1" in
--write) WRITE="${2:?}"; shift 2 ;;
--write)
WRITE="${2:?}"
shift 2
;;
--next)
if [[ $# -ge 2 && "$2" != -* ]]; then
NEXT="$2"
shift 2
else
NEXT="patch"
shift
fi
;;
-h|--help)
sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
*)
@@ -25,17 +47,80 @@ while [[ $# -gt 0 ]]; do
esac
done
if [[ -z "${RAW}" ]]; then
bump_semver() {
local base="$1" which="${2:-patch}"
local ma mi pa
base="${base#v}"
base="${base%%[-+]*}"
if [[ ! "${base}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "error: cannot bump non-semver base '${base}'" >&2
exit 1
fi
IFS=. read -r ma mi pa <<<"${base}"
ma="${ma:-0}"
mi="${mi:-0}"
pa="${pa:-0}"
case "${which}" in
major) ma=$((ma + 1)); mi=0; pa=0 ;;
minor) mi=$((mi + 1)); pa=0 ;;
patch) pa=$((pa + 1)) ;;
*)
echo "error: unknown bump '${which}' (want patch|minor|major)" >&2
exit 1
;;
esac
printf '%s.%s.%s' "${ma}" "${mi}" "${pa}"
}
latest_semver_base() {
local tag=""
if git rev-parse --git-dir >/dev/null 2>&1; then
tag="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname 2>/dev/null | head -1 || true)"
fi
if [[ -n "${tag}" ]]; then
printf '%s\n' "${tag#v}"
return 0
fi
if [[ -f VERSION ]]; then
local v
v="$(tr -d '[:space:]' <VERSION)"
v="${v#v}"
if [[ -n "${v}" ]]; then
printf '%s\n' "${v}"
return 0
fi
fi
printf '0.0.0\n'
}
if [[ -n "${NEXT}" ]]; then
if [[ -n "${RAW}" ]]; then
echo "error: pass either <raw> or --next, not both" >&2
exit 2
fi
case "${NEXT}" in
patch|minor|major) ;;
*)
echo "error: --next wants patch|minor|major (got '${NEXT}')" >&2
exit 1
;;
esac
BASE="$(latest_semver_base)"
VER="$(bump_semver "${BASE}" "${NEXT}")"
echo "Auto-bumped ${NEXT}: ${BASE} → ${VER}" >&2
elif [[ -n "${RAW}" ]]; then
RAW="$(printf '%s' "${RAW}" | tr -d '[:space:]')"
VER="${RAW#v}"
if [[ ! "${VER}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][A-Za-z0-9.+-]*)?$ ]]; then
echo "error: invalid version '${RAW}' (want X.Y.Z or vX.Y.Z)" >&2
exit 1
fi
else
echo "usage: $0 [--write VERSION] <raw-version>" >&2
echo " $0 [--write VERSION] --next [patch|minor|major]" >&2
exit 2
fi
RAW="$(printf '%s' "${RAW}" | tr -d '[:space:]')"
VER="${RAW#v}"
if [[ ! "${VER}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.+-][A-Za-z0-9.+-]*)?$ ]]; then
echo "error: invalid version '${RAW}' (want X.Y.Z or vX.Y.Z)" >&2
exit 1
fi
TAG="v${VER}"
echo "VERSION=${VER}"