diff --git a/.github/check-version.sh b/.github/check-version.sh index ca236e3..0034dee 100755 --- a/.github/check-version.sh +++ b/.github/check-version.sh @@ -7,11 +7,16 @@ # MAJOR.MINOR.PATCH without leading zeros, greater than the base one, and # have its section in CHANGELOG.md ("## — YYYY-MM-DD") that the # base branch does not have yet. The sources, android/ (except its unit -# tests), the Makefile, ci/ and scripts/ count as code or build, without the -# Markdown files in them (server/README.md, android/README.md); docs, -# tests/, bench/, screenshots and the workflows in .github/ need no new -# version. The format is checked only when the version changes, so the -# MAJOR.MM versions up to 1.12 stay valid until the next code change. +# tests), the Makefile, ci/, scripts/ and the build workflow +# (.github/workflows/build.yml) count as code or build, without the Markdown +# files in them (server/README.md, android/README.md); docs, tests/, bench/, +# screenshots and the rest of .github/ need no new version. The script does +# not tell the build steps of the workflow from its test or release steps: +# 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 release step, a comment). +# The format is checked only when the version changes, so the MAJOR.MM +# versions up to 1.12 stay valid until the next code change. # # Usage: .github/check-version.sh [BASE] (default origin/main; CI passes # HEAD^1, the base side of the pull request's merge commit) @@ -21,7 +26,7 @@ set -eu base=${1:-origin/main} header=include/vtesse/version.h -code="include netcore cli vtun port server scripts third_party tools android Makefile ci" +code="include netcore cli vtun port server scripts third_party tools android Makefile ci .github/workflows/build.yml" # The one VTESSE_VERSION definition (comments and other text do not count). ver() { @@ -42,9 +47,16 @@ changed=$(git diff --name-only "$base"...HEAD -- $code ':(exclude)android/app/sr ':(exclude,glob)**/*.md') if [ "$new" = "$old" ]; then + if [ -n "$changed" ] && [ "${NO_RELEASE:-}" = true ]; then + echo "::notice::code or build changed, VTESSE_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, VTESSE_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 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7184eb..ec7bcc1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,9 @@ # MAJOR.MINOR.PATCH) and its notes are one short line; the change history lives # in CHANGELOG.md. No versioned releases are kept. # On pull requests, .github/check-version.sh checks that a change to the code or -# the build comes with a new VTESSE_VERSION and its CHANGELOG.md section. +# the build (this workflow included) comes with a new VTESSE_VERSION and its +# CHANGELOG.md section, unless the pull request is labelled no-release: a change +# that leaves the release files as they are. Only the head of main is published. # name: VTesse CI @@ -28,6 +30,8 @@ on: branches: [main] pull_request: branches: [main] + # labeled / unlabeled: the version check reads the no-release label + types: [opened, synchronize, reopened, labeled, unlabeled] workflow_dispatch: # One run per branch: on pull requests a newer push cancels the older run; on @@ -55,6 +59,9 @@ jobs: # base it was made from, so both versions come from the same base. - name: Code changes come with a new version run: .github/check-version.sh HEAD^1 + 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') }} # ------------------------------------------------------------------ tests tests: @@ -244,12 +251,28 @@ jobs: cp scripts/vtesse-hook.sh scripts/vtesse-udhcpc.script scripts/S99vtesse scripts/vtesse.init release/ ls -l release + # Only the head of main is published: a re-run of an older run, or a run that + # finishes after a newer push, must not put an older commit back as "latest". + # Checked last, right before anything is deleted. - name: Publish "latest" + id: publish env: GH_TOKEN: ${{ github.token }} VER: ${{ steps.vars.outputs.ver }} VTUN: ${{ steps.vars.outputs.vtun }} run: | + # a failed or empty lookup fails the step: only a newer head skips the publication + ref=$(git ls-remote --exit-code origin refs/heads/main) || + { echo "::error::cannot read the head of main"; exit 1; } + head=$(printf '%s\n' "$ref" | cut -f 1) + if ! printf '%s\n' "$head" | grep -Eqx '[0-9a-f]{40}'; then + echo "::error::unexpected head of main: \"$head\""; exit 1 + fi + if [ "$head" != "$GITHUB_SHA" ]; then + echo "::notice::main is at ${head:0:7} now, not ${GITHUB_SHA::7}: not published" + exit 0 + fi + echo "published=true" >> "$GITHUB_OUTPUT" gh release delete latest --cleanup-tag --yes 2>/dev/null || true gh release create latest release/* \ --latest \ @@ -260,7 +283,7 @@ jobs: # The Telegram channel gets the APK of every new version, as in OpenIPC/decoder (#33). # Organization secrets; a failed upload is reported but does not fail the release. - name: Telegram - if: steps.vars.outputs.new == 'true' + if: steps.vars.outputs.new == 'true' && steps.publish.outputs.published == 'true' env: TG_TOKEN: ${{ secrets.TELEGRAM_TOKEN_BOT_OPENIPC }} TG_CHANNEL: ${{ secrets.TELEGRAM_CHANNEL_OPENIPC_DEV }}