Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 46 additions & 19 deletions .github/check-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,73 @@
#
# Copyright (c) OpenIPC https://openipc.org MIT License
#
# check-version.sh — fail a pull request that changes code or the build
# but not CAMEX_VERSION in src/version.h (#63). The new version must be
# MAJOR.MINOR.PATCH without leading zeros, greater than the base one,
# and have its section in CHANGELOG.md ("## <version> — <date>").
# src/, android/, the Makefile, ci/ and scripts/ count as code or build;
# docs, tests, screenshots and the workflows in .github/ do not need a new
# version. Same script as OpenIPC/vectis, other paths and version macro.
# check-version.sh — fail a pull request that changes the code or the build
# but not CAMEX_VERSION in src/version.h. A new version must be
# MAJOR.MINOR.PATCH without leading zeros, greater than the base one, and
# have its section in CHANGELOG.md ("## <version> — YYYY-MM-DD") that the
# base branch does not have yet. src/, android/, the Makefile, ci/,
# scripts/ and the build workflow (.github/workflows/build.yml) count as code
# or build, without the Markdown files in them; docs, tests, screenshots and
# the rest of .github/ (this check and its workflow, version.yml, included)
# need no new version. NO_RELEASE=true (the pull request's `no-release`
# label) lets a change to the code or the build through without a new
# version, for changes that leave the release files as they are (a test or
# publication step, a build-script tidy-up, a comment).
#
# Usage: .github/check-version.sh [BASE] (default origin/master)
# Usage: .github/check-version.sh [BASE] (default origin/master; CI passes
# HEAD^1, the base side of the pull request's merge commit)
#

set -eu

base=${1:-origin/master}
code="src android Makefile ci scripts"
header=src/version.h
code="src android Makefile ci scripts .github/workflows/build.yml"

ver() { sed -n 's/^#define CAMEX_VERSION *"\(.*\)"/\1/p'; }

new=$(ver < src/version.h)
old=$(git show "$base:src/version.h" | ver)
# The one CAMEX_VERSION definition. Every "#define CAMEX_VERSION" counts,
# however it is spaced ("# define" too), and it must be the plain form
# '#define CAMEX_VERSION "X.Y.Z"' that the release job reads.
ver() {
h=$(cat)
n=$(printf '%s\n' "$h" | grep -cE '^[[:space:]]*#[[:space:]]*define[[:space:]]+CAMEX_VERSION([[:space:]]|$)' || true)
v=$(printf '%s\n' "$h" | sed -n 's/^#define CAMEX_VERSION "\([^"]*\)"[[:space:]]*$/\1/p')
if [ "$n" -ne 1 ] || [ -z "$v" ]; then
echo "::error::$1:$header: expected one '#define CAMEX_VERSION \"X.Y.Z\"'," \
"found $n definition(s)" >&2
exit 1
fi
printf '%s\n' "$v"
}

if ! echo "$new" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then
echo "::error::CAMEX_VERSION \"$new\" is not MAJOR.MINOR.PATCH"
exit 1
fi
new=$(ver HEAD < "$header")
old=$(git show "$base:$header" | ver "$base")

# shellcheck disable=SC2086 # $code is a list of paths
changed=$(git diff --name-only "$base"...HEAD -- $code)
changed=$(git diff --name-only "$base"...HEAD -- $code ':(exclude,glob)**/*.md')

if [ "$new" = "$old" ]; then
if [ -n "$changed" ] && [ "${NO_RELEASE:-}" = true ]; then
echo "::notice::code or build changed, CAMEX_VERSION stays $old: the no-release" \
"label says the release files do not change"
echo "$changed"
exit 0
fi
if [ -n "$changed" ]; then
echo "::error::code or build changed, CAMEX_VERSION is still $old:" \
"bump PATCH (fix) or MINOR (feature) and add its CHANGELOG.md section"
"bump PATCH (fix) or MINOR (feature) and add its CHANGELOG.md section," \
"or label the pull request no-release if the release files do not change"
echo "$changed"
exit 1
fi
echo "version $new unchanged, no code or build changes"
exit 0
fi

if ! echo "$new" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then
echo "::error::CAMEX_VERSION \"$new\" is not MAJOR.MINOR.PATCH"
exit 1
fi

top=$(printf '%s\n%s\n' "$old" "$new" | sort -V | tail -n 1)
if [ "$top" != "$new" ]; then
echo "::error::CAMEX_VERSION $new is not greater than $old"
Expand Down
20 changes: 2 additions & 18 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,8 @@ env:
# actions target Node 24 natively (checkout@v5, upload-artifact@v5, download-artifact@v5, gh-release@v3)

jobs:
# ---------------------------------------------------------------- version
# Pull requests: a change to the code or the build comes with a new
# CAMEX_VERSION (MAJOR.MINOR.PATCH) and its CHANGELOG.md section (#63)
version:
name: Version bump
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Code changes come with a new version
env:
# through the environment, not pasted into the script
BASE: ${{ github.base_ref }}
run: .github/check-version.sh "origin/$BASE"
# The version check of pull requests runs in version.yml: a label change
# re-runs only that check, not the builds.

# ------------------------------------------------------------------ linux
linux:
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#
# Copyright (c) OpenIPC https://openipc.org MIT License
#
# version.yml — the version check of pull requests (#63, #68, #69)
#
# A change to the code or the build (build.yml included) comes with a new
# CAMEX_VERSION (MAJOR.MINOR.PATCH) and its CHANGELOG.md section, unless the
# no-release label says the release files do not change. A workflow of its
# own: adding or removing a label re-runs this check, not the builds.
#

name: version

on:
pull_request:
branches: [master, main]
# labeled / unlabeled: the check reads the no-release label
types: [opened, synchronize, reopened, labeled, unlabeled]

permissions:
contents: read

# a newer event of the same pull request cancels the older check
concurrency:
group: version-${{ github.ref }}
cancel-in-progress: true

jobs:
version:
name: Version bump
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

# The checkout is the merge commit; its first parent is the base it was made from.
- name: Code changes come with a new version
env:
# a change that leaves the release files as they are may skip the new version
NO_RELEASE: ${{ contains(github.event.pull_request.labels.*.name, 'no-release') }}
run: .github/check-version.sh HEAD^1
Loading