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
60 changes: 42 additions & 18 deletions .github/check-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,69 @@
#
# Copyright (c) OpenIPC https://openipc.org MIT License
#
# check-version.sh — fail a pull request that changes code or the build
# but not PROGRAM_VERSION in vectis.c. 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>").
# The sources, the Makefile and ci/ count as code or build; docs, tests,
# screenshots and the workflows in .github/ do not need a new version.
# check-version.sh — fail a pull request that changes the code or the build
# but not PROGRAM_VERSION in vectis.c. 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. The sources, the Makefile, ci/ and the
# build workflow (.github/workflows/build.yml: it builds the release files)
# count as code or build, without the Markdown files in them; docs, tests,
# screenshots and the rest of .github/ 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 in the
# workflow, 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="vectis.c vectis-bootrom.c usbserial.c usbserial.h ch34x.c Makefile ci"
source=vectis.c
code="vectis.c vectis-bootrom.c usbserial.c usbserial.h ch34x.c Makefile ci .github/workflows/build.yml"

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

new=$(ver < vectis.c)
old=$(git show "$base:vectis.c" | ver)
# The one PROGRAM_VERSION definition (comments and other text do not count).
ver() {
v=$(sed -n 's/^#define PROGRAM_VERSION[[:space:]]*"\([^"]*\)"[[:space:]]*$/\1/p')
if [ "$(printf '%s\n' "$v" | grep -c .)" -ne 1 ]; then
echo "::error::$1:$source: expected one PROGRAM_VERSION definition," \
"found: $(printf '%s' "$v" | tr '\n' ' ')" >&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::PROGRAM_VERSION \"$new\" is not MAJOR.MINOR.PATCH"
exit 1
fi
new=$(ver HEAD < "$source")
old=$(git show "$base:$source" | 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, PROGRAM_VERSION stays $old: the no-release" \
"label says the release files do not change"
echo "$changed"
exit 0
Comment thread
compot-dev marked this conversation as resolved.
Resolved
fi
if [ -n "$changed" ]; then
echo "::error::code or build changed, PROGRAM_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::PROGRAM_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::PROGRAM_VERSION $new is not greater than $old"
Expand Down
28 changes: 23 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
# (ci/build-static.sh) and soft-float mipsel for OpenWrt routers
# (ci/build-mipsel.sh).
# On pull requests, .github/check-version.sh checks that a change to the
# code or the build comes with a new PROGRAM_VERSION and its CHANGELOG.md
# section.
# code or the build (this workflow included) comes with a new
# PROGRAM_VERSION and its CHANGELOG.md section; the no-release label lets
# a change that keeps the release files as they are through without one.
# Only the head of master publishes, so a re-run of an older run never
# rolls "latest" back.
# Release: one release only, "latest", replaced by every push to master
# once all builds pass. Its title carries PROGRAM_VERSION ("Vectis 1.8.0"),
# its notes are short; the history is in CHANGELOG.md. No versioned
Expand All @@ -22,6 +25,8 @@ name: Build
on:
push:
pull_request:
# labeled / unlabeled: the version check reads the no-release label
types: [opened, synchronize, reopened, labeled, unlabeled]
workflow_dispatch:

# The build only reads the repository and uploads artifacts.
Expand All @@ -46,11 +51,12 @@ jobs:
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:
# through the environment, not pasted into the script
BASE: ${{ github.base_ref }}
run: .github/check-version.sh "origin/$BASE"
# 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

linux:
name: Linux ${{ matrix.platform.name }}
Expand Down Expand Up @@ -229,6 +235,18 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
# Only the head of master publishes: a re-run of an older run must not
# roll "latest" back to its commit. Checked here, right before the
# release is touched; a newer push's run waits for this one (concurrency)
# and publishes after it. A failed lookup fails the job, not skips it.
if ! head=$(git ls-remote --exit-code origin refs/heads/master); then
echo "::error::cannot read the head of master"; exit 1
fi
head=${head%%[[:space:]]*}
if [ "$head" != "$GITHUB_SHA" ]; then
echo "::notice::master is at ${head:0:7} now, not ${GITHUB_SHA::7}: not published"
exit 0
fi
# The release being replaced: its title tells Telegram whether this
# is a new version. "Not found" means there is none yet; any other
# failure stops here, before anything is deleted or announced.
Expand Down
Loading