Skip to content

ci: versions are MAJOR.MINOR.PATCH, a new one with every code change (#44) - #45

Merged
compot-dev merged 2 commits into
mainfrom
ci/version-check
Oct 17, 2026
Merged

compot-dev merged 2 commits into
mainfrom
ci/version-check

Conversation

@compot-dev

Copy link
Copy Markdown
Collaborator

Part of #44; the Android versionCode formula for three-part versions comes with #43, the first version written MAJOR.MINOR.PATCH (changing build.gradle.kts here would itself need a version bump).

What changes

  • .github/check-version.sh, run by a new version job on pull requests (base: HEAD^1 of the checked-out merge commit):
    • if the sources (include, netcore, cli, vtun, port, server, third_party, tools), android/ (except its unit tests), the Makefile, ci/ or scripts/ changed, VTESSE_VERSION must change too;
    • a new version must be MAJOR.MINOR.PATCH without leading zeros, greater than the base one (a MAJOR.MM base counts as MAJOR.MM.0, so 1.12.0 is 1.12), with a ## <version> — YYYY-MM-DD section the base branch does not have;
    • the format is checked only when the version changes: 1.12 stays valid until the next code change takes 1.12.1 or 1.13.0; only one #define VTESSE_VERSION line counts.
  • build.yml: on main a newer push now waits instead of cancelling a running workflow, so the release job is never stopped between deleting the old latest and creating the new one; a manual run shares the push group (it had a group of its own and could publish at the same time).
  • docs/testing.md ("Versions and releases") and the CHANGELOG.md header: the rule (PATCH, MINOR; MAJOR only when the maintainer decides; docs, tests/, bench/, .github/ need no bump) and where the scheme changes.

Tests

compot/cross on a scratch clone, 14 cases:

Case Expected Result
no change / docs / tests/ + bench/ / android unit tests pass pass
netcore, ci/ or a version.h comment without a bump fail fail
1.13 (old style) / 1.12.0 / 1.13.0 without a section / 1.11.9 fail fail
1.12.1 and 1.13.0 with a section pass pass
a merge commit checked with HEAD^1 pass pass

shellcheck v0.10.0 -S style: clean. .github/, docs and CHANGELOG.md only: no bump needed for this PR.

…44)

.github/check-version.sh, run by a new pull-request job (base: HEAD^1 of
the merge commit): if the sources (include, netcore, cli, vtun, port,
server, third_party, tools), android/ (except its unit tests), the
Makefile, ci/ or scripts/ changed, VTESSE_VERSION must change too. A
new version must be MAJOR.MINOR.PATCH without leading zeros, greater
than the base one (a MAJOR.MM base counts as MAJOR.MM.0, so 1.12.0 is
1.12), with a CHANGELOG.md section of its own. The format is checked
only when the version changes, so 1.12 stays valid until the next code
change (1.12.1 or 1.13.0). Only a #define line counts, exactly one.

build.yml: on main a newer push waits instead of cancelling (the release
job deletes and recreates "latest"), a manual run shares the push
group. docs/testing.md and CHANGELOG.md: the rule and where the scheme
changes.

The Android versionCode formula for three parts comes with the next
version (#43), which is the first one written MAJOR.MINOR.PATCH.

Tested in compot/cross on a scratch clone, 14 cases (docs, tests/,
bench/ and android unit tests pass; code, ci/ and a version.h comment
without a bump fail; 1.13, 1.12.0, a missing section and 1.11.9 fail;
1.12.1 and 1.13.0 with a section pass; a merge commit with HEAD^1
passes). shellcheck v0.10.0 clean.

Refs #44
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Enforce version bumps for code changes and serialize main releases

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Require a newer MAJOR.MINOR.PATCH version and new changelog section for code or build changes.
• Add a pull-request version check and prevent main releases from interrupting one another.
• Document the versioning policy and the transition from two-part versions.
Diagram

graph TD
  PR["PR merge"] --> Check["Version check"] --> Header["Version header"] --> Changelog["Changelog section"] --> Status["PR check status"]
  Push["Main pushes"] --> Queue["Serialized workflows"] --> Release["Latest release"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Serialize only the release job
  • ➕ Allows builds for newer main commits to run concurrently.
  • ➖ Needs an additional stale-build guard so an older build cannot publish after a newer one.

Recommendation: Keep workflow-level serialization for now: it directly protects the existing delete-and-recreate release step and favors the newest waiting run. Consider release-only serialization if queued build time becomes significant.

Files changed (4) +125 / -12

Enhancement (1) +83 / -0
check-version.shValidate pull-request version bumps +83/-0

Validate pull-request version bumps

• Checks whether source or build paths changed relative to the base. Requires a single, increasing three-part VTESSE_VERSION and a new dated changelog section when the version changes, while accepting unchanged legacy versions for exempt changes.

.github/check-version.sh

Documentation (2) +14 / -5
CHANGELOG.mdRecord the transition to three-part versions +3/-1

Record the transition to three-part versions

• Clarifies that versions after 1.12 use MAJOR.MINOR.PATCH while existing changelog headings retain their released numbers.

CHANGELOG.md

testing.mdDocument version and release rules +11/-4

Document version and release rules

• Describes bump levels, paths that require a bump, the changelog requirement, and the legacy-version transition. Clarifies how the latest release is titled.

docs/testing.md

Other (1) +28 / -7
build.ymlRun version checks and protect release publication +28/-7

Run version checks and protect release publication

• Adds a pull-request job that checks the merge commit against its first parent. Changes concurrency so main runs, including manually triggered runs in the same group, do not cancel an active workflow.

.github/workflows/build.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. CI-only changes force a version bump 📎 Requirement gap ≡ Correctness
Description
The code path list includes ci, so changes under ci/ populate changed and require a new
VTESSE_VERSION. A pull request that changes only CI files therefore reaches the unchanged-version
error even though CI-only changes are exempt from version bumps.
Code

.github/check-version.sh[24]

+code="include netcore cli vtun port server scripts third_party tools android Makefile ci"
Evidence
The checklist requires CI-only pull requests to be exempt from version bumps, but the added path
list explicitly includes ci, and the unchanged-version branch fails whenever a matching path
changes.

Enforce version bumps in pull-request CI
.github/check-version.sh[24-24]
.github/check-version.sh[44-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The version check treats changes under `ci/` as code changes, requiring a version bump for CI-only pull requests even though the compliance policy exempts CI-only changes.
## Fix Focus Areas
- .github/check-version.sh[24-24]
## Recommended Fix
Remove CI-only paths from the set of paths that require a version bump, while continuing to check the source and build paths that the policy identifies as versioned code.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. A manual run can suppress a new release ✓ Resolved
Description
The concurrency group puts manual runs on main in the same queue as pushes, where a newly queued
manual run can replace a pending push. If one push is running and a newer push is pending,
dispatching a manual run cancels the pending push; the manual run skips the release job, so latest
remains at the older push.
Code

.github/workflows/build.yml[R39-40]

+  group: build-${{ github.event_name == 'pull_request' && 'pull_request' || 'push' }}-${{ github.ref }}
+  cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
Evidence
The workflow accepts manual dispatches and maps every non-PR run on the main ref to the push
concurrency group. Main runs do not cancel the running run, but a newly queued run replaces an
existing pending run in that group; the release job's condition then excludes the manual run.

.github/workflows/build.yml[27-40]
.github/workflows/build.yml[204-209]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A manual run can replace a pending main push in the shared concurrency group, but only the push publishes `latest`.
## Fix Focus Areas
- .github/workflows/build.yml[33-40]
- .github/workflows/build.yml[204-209]
## Recommended Fix
Use separate workflow concurrency groups for manual and push runs, and serialize release publication separately if both event types could publish in the future. Ensure a manual run cannot discard a pending publishing push.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Patch releases cannot upgrade Android apps 🐞 Bug ≡ Correctness
Description
check-version.sh accepts a greater three-part version such as 1.12.1, but Android's
versionCode calculation reads only the major and minor components. If a patch bump is merged
before the planned Android formula change, the published APK has the same version code as 1.12 and
cannot upgrade an existing installation.
Code

.github/check-version.sh[55]

+if ! echo "$new" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then
Evidence
The added format and ordering checks permit 1.12.1 after 1.12, while the current Android build
computes 1*100+12 for both versions.

.github/check-version.sh[55-69]
android/app/build.gradle.kts[9-11]
android/app/build.gradle.kts[26-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The checker approves patch releases while Android still calculates the same version code for every patch of a minor version.
## Fix Focus Areas
- .github/check-version.sh[55-69]
- android/app/build.gradle.kts[29-31]
## Recommended Fix
Ensure the first three-part version bump also updates Android's versionCode formula, or make the checker reject patch bumps until that migration is present. Verify that an APK for 1.12.1 has a higher versionCode than the APK for 1.12.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Some documentation edits require a bump ✓ Resolved
Description
The code path list includes entire source directories, including documentation files such as
server/README.md and android/README.md. A pull request changing only one of those files reaches
the unchanged-version failure even though the documented rule exempts documentation changes.
Code

.github/check-version.sh[23]

+code="include netcore cli vtun port server scripts third_party tools android Makefile ci"
Evidence
The selected paths include server and android wholesale, and any matching change with an
unchanged version fails. Both directories contain README files, while the version policy exempts
documentation.

.github/check-version.sh[9-12]
.github/check-version.sh[23-43]
server/README.md[1-1]
android/README.md[1-1]
docs/testing.md[44-51]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Documentation-only changes inside selected source directories are treated as code changes and require an unintended version bump.
## Fix Focus Areas
- .github/check-version.sh[23-43]
## Recommended Fix
Filter documentation paths from the changed-file list before enforcing a version bump, while retaining source and build files in those directories. Add a check for a documentation-only change under a selected directory.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can see how risky a PR is at a glance with blast radius labels

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/build.yml
Comment thread .github/check-version.sh
…s no bump

Qodo on #45:
- a manual run shares the push group on main and can take the place of
  a waiting push, but the release job ran for pushes only, so latest
  could stay at an older commit. It runs for manual runs of main too.
- server/README.md and android/README.md sit in code directories: the
  version check leaves out Markdown files there.

Tested: a change to the two READMEs only passes without a bump; the 14
earlier cases unchanged. shellcheck clean.

Refs #44
@compot-dev

Copy link
Copy Markdown
Collaborator Author

Qodo findings, both fixed in e41438f:

  1. A manual run can suppress a new release: the release job now runs for any non-PR run of main (github.event_name != 'pull_request'), so a manual run that takes the place of a waiting push publishes latest itself (it builds the current head). The Telegram step still posts only a new version (title comparison), so a manual rebuild of the same version is not announced.
  2. Some documentation edits require a bump: the check leaves out Markdown files in the code directories (:(exclude,glob)**/*.md: server/README.md, android/README.md). Tested: a change to those two only passes without a bump; the 14 earlier cases are unchanged. shellcheck clean.

@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

Comment thread .github/check-version.sh

base=${1:-origin/main}
header=include/vtesse/version.h
code="include netcore cli vtun port server scripts third_party tools android Makefile ci"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Ci-only changes force a version bump 📎 Requirement gap ≡ Correctness

The code path list includes ci, so changes under ci/ populate changed and require a new
VTESSE_VERSION. A pull request that changes only CI files therefore reaches the unchanged-version
error even though CI-only changes are exempt from version bumps.
Agent Prompt
## Issue description
The version check treats changes under `ci/` as code changes, requiring a version bump for CI-only pull requests even though the compliance policy exempts CI-only changes.

## Fix Focus Areas
- .github/check-version.sh[24-24]

## Recommended Fix
Remove CI-only paths from the set of paths that require a version bump, while continuing to check the source and build paths that the policy identifies as versioned code.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .github/check-version.sh
exit 0
fi

if ! echo "$new" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Patch releases cannot upgrade android apps 🐞 Bug ≡ Correctness

check-version.sh accepts a greater three-part version such as 1.12.1, but Android's
versionCode calculation reads only the major and minor components. If a patch bump is merged
before the planned Android formula change, the published APK has the same version code as 1.12 and
cannot upgrade an existing installation.
Agent Prompt
## Issue description
The checker approves patch releases while Android still calculates the same version code for every patch of a minor version.

## Fix Focus Areas
- .github/check-version.sh[55-69]
- android/app/build.gradle.kts[29-31]

## Recommended Fix
Ensure the first three-part version bump also updates Android's versionCode formula, or make the checker reject patch bumps until that migration is present. Verify that an APK for 1.12.1 has a higher versionCode than the APK for 1.12.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e41438f

@compot-dev

Copy link
Copy Markdown
Collaborator Author

Qodo findings on e41438f:

  • 1. CI-only changes force a version bump: by design, not adopted. ci/ holds the release build (ci/build-musl.sh builds and packs the binaries, the Dockerfile.* define the build images), so a change there changes what is shipped and counts as the build; "CI-only" in the rule means the workflows in .github/. docs/testing.md and the script header say so; OpenIPC/vectis does the same.
  • 3. Patch releases cannot upgrade Android apps: right for a PATCH-only bump before the formula change (1.12.1 would keep versionCode 112). The next PR, Release: one binary per platform, drop vtesse-tap-<arch> #43, takes 1.13.0 (versionCode stays above 112 even with the old formula) and brings the MAJOR*10000 + MINOR*100 + PATCH formula with it; the formula cannot go here without a version bump of its own (android/ is code). There are no other open PRs in this repository, and Release: one binary per platform, drop vtesse-tap-<arch> #43 follows right after this one.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Qodo findings on e41438f:

  • 1. CI-only changes force a version bump: by design, not adopted. ci/ holds the release build (ci/build-musl.sh builds and packs the binaries, the Dockerfile.* define the build images), so a change there changes what is shipped and counts as the build; "CI-only" in the rule means the workflows in .github/. docs/testing.md and the script header say so; OpenIPC/vectis does the same.
  • 3. Patch releases cannot upgrade Android apps: right for a PATCH-only bump before the formula change (1.12.1 would keep versionCode 112). The next PR, Release: one binary per platform, drop vtesse-tap-<arch> #43, takes 1.13.0 (versionCode stays above 112 even with the old formula) and brings the MAJOR*10000 + MINOR*100 + PATCH formula with it; the formula cannot go here without a version bump of its own (android/ is code). There are no other open PRs in this repository, and Release: one binary per platform, drop vtesse-tap-<arch> #43 follows right after this one.

Your clarification changes the assessment of both findings:

  • finding 1 — CI-only changes force a version bump: This appears to be a false positive. Given the documented policy, ci/ contains release/build inputs and therefore is intentionally versioned; the exemption applies to .github/ workflows. The script and documentation are consistent with that interpretation.
  • finding 3 — Patch releases cannot upgrade Android apps: The technical issue is real for a 1.12.1 release: the current Android formula would still produce versionCode 112. Relying on a subsequent PR to change the formula leaves this PR unsafe if it independently permits a patch release. The sequencing and absence of competing PRs reduce the practical risk, but do not remove the correctness gap in this PR’s version policy.

I can’t verify or change finding state from this comment alone; no dismissal was requested.

@compot-dev
compot-dev merged commit 7663ce5 into main Oct 17, 2026
10 checks passed
@compot-dev
compot-dev deleted the ci/version-check branch October 17, 2026 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant