feat: implement dynamic versioning from Git tags #42
No reviewers
Labels
No Label
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: sarman/tftsr-devops_investigation#42
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/version-dynamic-build"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
🤖 Automated PR Review:\n\nHere's a comprehensive review of the PR "feat: implement dynamic versioning from Git tags", covering correctness, security, and best practices:
✅ 1. Summary
This PR implements dynamic version injection from Git tags into the application build, replacing hardcoded versions in multiple configuration files (
Cargo.toml,package.json,tauri.conf.json) and exposing the version via a new Tauri command (get_app_version). It also cleans up outdated entries fromCHANGELOG.md.The approach is largely sound and follows common patterns (e.g., using
git describe --tags), but several critical correctness and security issues must be addressed before merging.⚠️ 2. Bugs & Correctness Issues
🔴 Critical:
npm ciin CI workflow may fail ifpackage-lock.jsonis outdatednpm cirequires an exactpackage-lock.jsonmatch. If the lockfile is not up-to-date (e.g., after merging unrelated PRs), this will fail.npm install(or ensure lockfile is regenerated before this step): Or better yet: move version update before dependency installation.🔴 Critical:
git describe --tags --abbrev=0may fail in shallow clones or detached HEADfetch-depth: 1) or detached HEAD states.git describerequires full history and tags to work. If tags aren’t fetched (common in PRs from forks), this will fall back silently to0.2.50, leading to incorrect versioning.update-version.mjsandbuild.rs, add robustness:.git/refs/tagsexists before runninggit describe.git describe --tags --always(commit hash) if tags are missing.🟠 Medium: Regex replacement in
update-version.mjsis fragileversion = '0.2.50').version = "..."lines (e.g., in[package]and[dependencies.trcaa]), replacing the wrong one.toml-rs) forCargo.toml.JSON.parse/JSON.stringifyforpackage.jsonandtauri.conf.json.semverpackage:🟠 Medium:
build.rsdoesn’t updateCargo.lockCargo.lockstill hasversion = "0.2.50"fortrcaa, butCargo.tomlis updated to0.2.62. This causes inconsistency and may break reproducible builds.Cargo.lockafter version update in CI:🟠 Medium:
get_version_from_git()inbuild.rsmay return invalid versiongit describereturnsv0.2.62-abc123(dirty tag),.trim_start_matches('v')yields0.2.62-abc123, which is not a valid semver. Tauri/Rust may reject it.git describe --tags --abbrev=0only when tags exist, and fallback to0.2.50if not:🛡️ 3. Security Issues
🔴 Critical: Command injection via untrusted Git tags
Problem: If an attacker creates a malicious Git tag (e.g.,
v1.2.3; rm -rf /),git describewill output it. WhileexecSyncruns the command, the output is used directly in regex replacement. However, the bigger risk is inbuild.rs:If
output.stdoutcontains newlines or control characters, it could corruptAPP_VERSION, but more critically:git describeis misconfigured (e.g.,core.abbrev=0+ bad ref), it could output arbitrary text.git describecould be replaced with a malicious script that outputs shell metacharacters — but this is a broader infrastructure risk.Mitigation:
semver:🟠 Medium:
npm ciwith--legacy-peer-depsmay install vulnerable packages--legacy-peer-depsbypasses peer dependency resolution, potentially installing outdated/unpatched packages.npm installor ensure lockfile is up-to-date. Better yet: pinnpmversion and avoid legacy flags.📜 4. Best Practices & Recommendations
✅ Good Practices
scripts/, build-time inbuild.rs.0.2.50(though should be configurable).@tauri-apps/api/appwhich may not reflect build-time version).CHANGELOG.md(removing outdated entries).📝 Suggestions for Improvement
Use
git describewith--matchto filter tagsPrevent accidental matches on non-version tags:
Add version validation in CI
Add a step to verify version is semver:
Avoid modifying
Cargo.lockat runtimeInstead of regenerating
Cargo.lockin CI, consider:cargo update -p trcaato update just the affected package.Document versioning policy
Add to
CONTRIBUTING.md:Use
tauri.conf.jsonschemaEnsure
tauri.conf.jsonhas"version"in the correct location (e.g.,package.version, nottauri.bundle.version— check Tauri docs).Add unit tests for
update-version.mjsMock
execSyncand test edge cases (no tags, invalid tags, etc.).✅ Final Recommendation
Do not merge as-is. Fix the critical issues first:
fetch-depth: 0).Cargo.lockafter version update.Once fixed, this is a solid, production-ready feature.
Let me know if you'd like help drafting the corrected
update-version.mjsor CI workflow!\n\n---\nthis is an automated review from Ollama🤖 Automated PR Review:\n\n### Code Review: Dynamic Versioning from Git Tags
1. Summary
This PR implements a robust, Git-tag-driven versioning system across the frontend (Node.js), Rust backend (Cargo), and build pipeline (CI). Key changes include:
scripts/update-version.mjsscript that reads Git tags, validates semver, and updatespackage.json,Cargo.toml,tauri.conf.json, andCargo.lock.build.rsscript that setsAPP_VERSIONat compile time (fallback to"0.2.50").get_app_version()for runtime version retrieval.--lockedflags (⚠️ concern).package.jsonand Cargo files to0.2.62.The implementation is largely well-structured and follows best practices for version extraction and propagation. However, several critical and high-severity issues must be addressed before merging.
2. Bugs / Errors
❌ Critical:
--lockedremoved fromcargo fmt,clippy,testProblem: Removing
--lockedmeanscargowill re-resolve dependencies, potentially pulling newer versions than those inCargo.lock. This defeats the purpose of having a lockfile and can lead to:Fix: Re-add
--lockedto allcargocommands in CI:⚠️ Medium:
Cargo.lockupdate logic inupdate-version.mjsis fragileThe
updateCargoLock()function uses regex-based line-by-line parsing:Problems:
versionfields in same package).trcaa— but what if other packages need version sync? (e.g.,tauri-buildmay embed version).Cargo.toml’sversionfield — butupdateTOML()is called separately forCargo.toml, so this is redundant and confusing.Fix:
cargo metadata --format-version=1+jqor a TOML parser (e.g.,toml-edit) instead of regex.updateCargoLock()entirely —cargo generate-lockfilealready regeneratesCargo.lockwith correct versions afterCargo.tomlis updated. SinceCargo.lockis regenerated afterupdate-version.mjs, the lockfile will reflect the new version automatically.⚠️ Medium: Git tag parsing assumes
git describe --tagsworks in CIProblem: In shallow clones (default in many CI systems),
git describemay fail or return incorrect tags unless--tagsand--abbrev=0are combined with--alwaysor depth is increased.Fix: In CI workflow, add:
Or in Gitea Actions (which this PR uses), ensure the checkout action fetches tags:
⚠️ Low: Fallback version
0.2.50is hardcoded in multiple placesscripts/update-version.mjsfallback:'0.2.50'build.rsfallback:"0.2.50".to_string()package.jsonwas0.2.50before this PR.Risk: If versioning fails (no tags, shallow clone, detached HEAD), all components fall back to
0.2.50, which may be outdated or mismatched with the actual state.Fix:
package.json’sversionfield).|| exit 1inupdate-version.mjs).3. Security Issues
❌ Critical:
execSyncinupdate-version.mjslacks input sanitizationProblem: While
git describeoutput is trusted,execSyncis used withcwd: projectRoot. IfprojectRootis compromised (e.g., symlinked to/tmp), malicious binaries could be executed.Mitigation:
shell: false(default in Node.jsexecSync) — ✅ already done.projectRootis a Git repo:⚠️ Medium:
tauri.conf.jsonbeforeBuildCommandrunsnpm run version:updateRisk: If
version:updatemodifiesCargo.toml, and the build runs in a different environment (e.g., Docker, cross-platform), the version may be inconsistent.Fix:
version:updateis idempotent and deterministic.build.rsonly (no JS script), but current dual approach is acceptable if CI enforces Git tags.4. Best Practices
✅ Good: Semver validation
✅ Validates major.minor.patch only — avoids
-beta,+build, etc.✅ Good: Rust
build.rssetsAPP_VERSIONat compile timeThis avoids runtime
gitcalls in production binaries (safer, faster).✅ Good: Tauri command fallbacks to
CARGO_PKG_VERSION✅ Graceful fallback if
APP_VERSIONisn’t set.❌ Missing: Version bump script should not commit
Cargo.lockchangesThe PR updates
Cargo.lockin the script, butCargo.lockshould not be manually edited — it’s auto-generated. Whilecargo generate-lockfileis run in CI, the script’s direct edit ofCargo.lockis unnecessary and risky.Fix: Remove
updateCargoLock()and rely oncargo generate-lockfile.✅ Good: Frontend uses Tauri command instead of deprecated
@tauri-apps/api/app✅
@tauri-apps/api/appis deprecated; using custom command is future-proof.❌ Missing: No tests for
scripts/update-version.mjsNo unit/integration tests for version parsing, fallbacks, or TOML/lockfile updates.
Fix: Add tests (e.g., with
vitestornode:test) for:.gitdirectory.✅ Good:
tauri.conf.jsonupdated to runversion:updatebefore buildEnsures version is set before frontend/Rust compilation.
🔧 Recommended Fixes (Prioritized)
--lockedto allcargoCI commands (critical).updateCargoLock()— rely oncargo generate-lockfile.fetch-depth: 0to checkout action (critical forgit describe)..gitdirectory existence check inupdate-version.mjs.0.2.50— use dynamic fallback (e.g.,package.jsonversion).Cargo.toml/Cargo.lock(already done — good!).✅ Final Verdict
Ready for merge? → No — fix the
--lockedomission and Git tag handling first.After fixes? → Strong candidate for merge. The implementation is thoughtful, well-structured, and aligns with modern CI/CD best practices.
Let me know if you'd like a revised patch with the recommended fixes!\n\n---\nthis is an automated review from Ollama
🤖 Automated PR Review:\n\n### Code Review: Dynamic Versioning from Git Tags
1. Summary
This PR introduces dynamic versioning derived from Git tags, replacing hardcoded versions in
package.json,Cargo.toml, andtauri.conf.jsonwith a script (scripts/update-version.mjs) that reads the latest Git tag (strippingvprefix), validates it as SemVer, and falls back topackage.jsonif unavailable. It also adds a Rust-side fallback viabuild.rs, and exposes the version via a Tauri command (get_app_version) for frontend use.✅ Positive aspects:
APP_VERSION) ensures version is available at runtime even if script fails.@tauri-apps/api/app.getVersion()(which is deprecated in Tauri v2).⚠️ However, several critical correctness, security, and maintainability issues exist.
2. Bugs & Errors
🔴 Critical:
Cargo.lockis modified at build time — violates reproducibility & breaks buildsCargo.lockis updated (e.g.,version = "0.2.50" → "0.2.62") by the script, butcargo generate-lockfileis run afterupdate-version.mjs.cargo generate-lockfileregeneratesCargo.lockfrom scratch (ignoring prior state), andcargo fmt --lockedexpects a stable lockfile. IfCargo.lockis modified beforegenerate-lockfile, the lockfile may be inconsistent or regenerated with wrong versions.update-version.mjsruns aftercargo generate-lockfile(e.g., in local dev), it changesCargo.lockwithout regenerating it → non-deterministic builds and potential CI failures.✅ Fix:
cargo generate-lockfilebefore version update, or better:Cargo.lockin CI or pre-build scripts. Instead:Cargo.lockversioned and only update it viacargo updatein controlled workflows.cargo buildregenerate it only if needed (e.g., in clean builds).cargo lockfile --lockedin CI to enforce consistency.🔴 Critical:
tauri.conf.jsonis TOML — but it's actually JSON! Script parses it as TOMLscripts/update-version.mjshasupdateTOML('src-tauri/tauri.conf.json', version), buttauri.conf.jsonis a JSON file, not TOML!^\s*version\s*=\s*"), which will never match JSON syntax ("version": "...").tauri.conf.jsonis not updated — version remains hardcoded (e.g.,"version": "0.2.50"), causing version mismatch between frontend/backend.✅ Fix:
updateTOML→updateJSONand fix the regex:updateJSON('src-tauri/tauri.conf.json', version).🟠 Minor:
build.rsfallback version is hardcodedbuild.rsreturns"0.2.50"as fallback — should match currentpackage.jsonversion to avoid drift.package.jsonis updated butbuild.rsfallback isn’t, builds may fail or use stale version.✅ Fix:
package.jsoninbuild.rs, like the JS script does:🟠 Minor:
Cargo.tomlhas extra blank lines addedCargo.toml— unnecessary and inconsistent with formatting.✅ Fix: Remove extra newlines.
3. Security Issues
🔴 Critical:
execSyncin Node.js script usesshell: falsebut no input sanitizationexecSync('git describe --tags --abbrev=0', { shell: false })is safe, but ifprojectRootis user-controllable, an attacker could inject malicious paths (e.g.,projectRoot = "/tmp; malicious_command").shell: falseprevents shell injection, path traversal or symlink attacks are still possible ifprojectRootis not validated.✅ Fix:
projectRootis an absolute path and exists:resolve(__dirname, '..')in production — useprocess.cwd()or a config.🟠 Medium:
update-version.mjswrites topackage.jsonwithout atomic writewriteFileSyncis not atomic — if interrupted (e.g., power loss),package.jsonmay be corrupted (partial write).✅ Fix:
fs.promises.writeFile+ temp file +rename):🟠 Medium: No validation of Git tag format beyond regex
isValidSemveronly checksX.Y.Z, but Git tags may include pre-release (-alpha.1) or build metadata (+build.123).v, it doesn’t handlev1.2.3-beta→"1.2.3-beta"failsisValidSemver, falls back topackage.json. This is intentional, but the warning is misleading.✅ Fix:
console.warn(Git tag "${version}" is not pure SemVer (X.Y.Z); using fallback. Full tag: ${output.trim()})/^0\.\d+\.\d+(?:-(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*))?$/).4. Best Practices
✅ Good: Centralized version logic
package.json.✅ Good: Rust
build.rssetsAPP_VERSION⚠️ Missing: CI workflow should validate version consistency
update-version.mjs, but no check ensurespackage.json,Cargo.toml, andtauri.conf.jsonversions match.✅ Fix:
⚠️ Missing: Script should be idempotent
npm run version:updatemultiple times should not cause side effects (e.g., re-updatingCargo.lockunnecessarily).✅ Fix:
⚠️ Missing: Documentation
git tag v0.2.62 && git push --tags)git tag -d v0.2.62 && git tag v0.2.62)✅ Fix:
CONTRIBUTING.md:⚠️ Minor:
CHANGELOG.mdtruncation## [0.2.55],## [0.2.54]).✅ Fix: Verify this is intentional (e.g., using
git-cliffto archive old releases). If not, restore entries.✅ Final Recommendations
Cargo.lockmodified in CIcargo generate-lockfile; use--lockedin CItauri.conf.jsonparsed as TOMLupdateJSON()for JSONbuild.rshardcoded fallbackpackage.jsonCargo.toml✅ Approve with Conditions
Do not merge until the critical bugs (TOML/JSON mismatch,
Cargo.lockhandling) are fixed. Once those are resolved, this is a solid, well-structured feature.\n\n---\nthis is an automated review from Ollama🤖 Automated PR Review:\n\n### Code Review Summary
This PR implements dynamic versioning from Git tags, updating version fields in
package.json,Cargo.toml, andtauri.conf.jsonvia a Node.js script and a Rust build script. It also removes the--lockedflag fromcargocommands, removes unused code fromazuredevops_search.rs, and adds aget_app_version()Tauri command.Below is a detailed review covering correctness, security, and best practices.
✅ 1. Summary of Changes
scripts/update-version.mjs: Node.js script that reads Git tags, validates semver, and updatespackage.json,Cargo.toml, andtauri.conf.json.src-tauri/build.rs: Rust build script setsAPP_VERSIONenv var at compile time (fallback to"0.2.50").src-tauri/src/commands/system.rs: Exposesget_app_version()to frontend via Tauri.--lockedfromcargo clippy/test(⚠️ concern).npm install+node scripts/update-version.mjsbefore Rust checks.azuredevops_search.rs.urldependency fromCargo.toml.0.2.50→0.2.62inpackage.json,Cargo.toml, andCargo.lock.⚠️ 2. Critical Bugs & Correctness Issues
🔴 A.
--lockedremoved fromcargocommandsProblem:
cargo clippy --lockedandcargo test --lockedensure reproducible builds by using exact versions fromCargo.lock. Removing--lockedrisks:mockito) behave differently across versions.Fix:
🔴 B.
tauri.conf.jsonis not a TOML fileProblem:
scripts/update-version.mjscallsupdateTOML('src-tauri/tauri.conf.json', version), buttauri.conf.jsonis JSON, not TOML. The current code assumes TOML syntax (version = "...") and will corrupt the JSON file.Example corruption:
Fix:
updateJson()function fortauri.conf.json.🟠 C.
Cargo.lockdependency removal (url) is unexplainedProblem:
url = "2.5.8"was removed fromCargo.toml, andCargo.lockupdated. But:urlbeing unused in code (unlessurlencodingfully replaced it).urlwas used elsewhere (e.g., parsing URLs), this may cause runtime panics.Fix:
urlis truly unused (e.g.,cargo tree -i url).urlencodingorurl’sserde-compatible features.🟠 D.
build.rsfallback version is hardcodedProblem:
build.rsfalls back to"0.2.50"instead of readingCargo.tomlorpackage.json. This defeats the purpose of dynamic versioning during local dev builds.Fix:
→ Prefer reading
Cargo.tomlfirst:🔒 3. Security Issues
🟡 A.
execSyncin Node.js script lacks input sanitizationProblem:
execSync('git describe --tags --abbrev=0')is safe ifprojectRootis trusted. But:projectRootis user-controlled (e.g., via symlink attacks), could execute arbitrary commands.shell: falseis correct, butcwd: projectRootcould be unsafe ifprojectRootis derived from untrusted input.Mitigation:
projectRootis resolved fromimport.meta.url(already done ✅).🟡 B.
JSON.parsewithout error boundary ingetFallbackVersion()Problem:
JSON.parse(content)may throw ifpackage.jsonis corrupted. Thecatchreturns'0.2.50', but logging is missing.Fix:
🛠️ 4. Best Practices & Recommendations
✅ A. Prefer Tauri’s built-in versioning
tauri.conf.json"version"field, andtauri buildautomatically injects it intoAPP_VERSION.Remove
update-version.mjsandbuild.rsversion logic. Instead:git describeto tag releases.npm version <tag>(which updatespackage.jsonand commits).package.json→tauri.conf.json→Cargo.toml(if synced viatauri.conf.json).✅ B. Validate Git tags more strictly
git describe --tagsmay return non-semver tags (e.g.,v0.2.62-3-gabc123).Current code only checks
git describe --tags --abbrev=0, which returns the latest tag, but:package.json.package.jsonis stale, version drift occurs.Better approach:
git describe --tags --dirtyto detect uncommitted changes.git describe --tags --match "v*"to filter tags.git tag --points-at HEADto ensure the commit is tagged.✅ C. Avoid manual TOML/JSON editing
Problem:
updateTOML()uses regex-like string replacement, which:Fix:
toml-rs(Rust) ortoml(Node.js) for safe TOML editing.tauri.conf.json, usetauri.conf.json’s"version"field and rely on Tauri’s tooling.✅ D. Add tests for
update-version.mjs.gitdirectory.package.json.jestornode:test.📌 Final Recommendations
--lockedremoval--lockedtauri.conf.jsonhandlingCargo.tomlurlusagerealpathSynccheck✅ Conclusion
The PR introduces a useful dynamic versioning feature but has critical correctness issues (
--lockedremoval, JSON/TOML confusion) and security risks (unvalidated exec). With fixes to the top issues, this would be a solid improvement. I recommend blocking merge until the critical bugs are resolved.Let me know if you'd like me to draft a PR review comment or suggest a revised
update-version.mjsimplementation.\n\n---\nthis is an automated review from Ollama