diff --git a/docs/ci/README.md b/docs/ci/README.md index 0fc234c0..7ea249e8 100644 --- a/docs/ci/README.md +++ b/docs/ci/README.md @@ -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) | diff --git a/docs/ci/templates/setup-release.yml b/docs/ci/templates/setup-release.yml index 1552476d..6abf0375 100644 --- a/docs/ci/templates/setup-release.yml +++ b/docs/ci/templates/setup-release.yml @@ -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:]' /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 diff --git a/runtime/runtime.cmake b/runtime/runtime.cmake index 75d01346..8d7f1e74 100644 --- a/runtime/runtime.cmake +++ b/runtime/runtime.cmake @@ -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() diff --git a/tools/ci/normalize_version.sh b/tools/ci/normalize_version.sh index 3274bf26..6836778a 100755 --- a/tools/ci/normalize_version.sh +++ b/tools/ci/normalize_version.sh @@ -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 # prints VERSION=… TAG=… -# normalize_version.sh --write VERSION # also writes VERSION file +# normalize_version.sh # prints VERSION=… TAG=… +# normalize_version.sh --write VERSION # 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:]' 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] " >&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}"