Aventuras/scripts/version.js
Tony Frazier afdb534071
Some checks are pending
CI / build-desktop (push) Waiting to run
CI / build-android (push) Waiting to run
Overhaul CI/CD: Node 24, shared release workflows, faster cached builds (#518)
## Summary

Node 24 migration, plus a CI/CD overhaul that puts desktop and Android
builds behind one shared,
cached, and now release-safe set of workflows — used by pull-request
validation, `master`, and
both release tracks alike.

## What changed

**Node & Actions**
- Node 24, tracked via `.nvmrc` (`package.json`'s `devEngines` enforces
it; the README points at
  `.nvmrc` instead of a hardcoded version)
- GitHub Actions bumped off versions that were 1-3 majors behind,
several still on the deprecated
  Node 20 runtime

**Shared, safer release workflows**
- Desktop and Android build jobs now live in reusable workflows
(`build-desktop.yml`,
`build-android.yml`), called by `master`/PR validation and both
`release.yml` and
`pre-release.yml` with a `publish`/`prerelease` switch — previously each
release workflow
  duplicated its own copy of the whole build matrix
- A `create-release` job creates the release once per run, so the build
matrix's parallel legs
  stop racing to create duplicate draft releases
- A release-safety guard now refuses to run when the tag's release is
already published, or when
a pushed tag doesn't match `tauri.conf.json`'s version — protects a
shipped release's assets and
  `prerelease` flag from a stray `workflow_dispatch` or job re-run
- Pre-releases are now created as a draft and only published once every
platform build succeeds,
  matching how stable releases already worked
- Runners are pinned (`macos-15`, `windows-2025`) instead of tracking
`-latest`, and the Linux
desktop build runs inside an `ubuntu:22.04` container, independent of
the `ubuntu-latest` host's
  own lifecycle

**CI now builds and caches on `master`**
- Every platform now builds on `master` (on push, a weekly schedule, and
manual dispatch) without
publishing, so tag-triggered release builds — which can only restore
caches from the tag itself
or the default branch — no longer start cold, and every push leaves a
downloadable per-platform
  build
- Rust, Gradle and npm all get real caches now; the npm cache key
ignores the version-bump commit
  every release makes, so releases hit the cache too
- `lint-and-typecheck.yml` now also runs `npm test`, which CI never did
before, and installs
  dependencies through the same cached action as the rest of CI

**Build changes**
- Android builds an APK only (the AAB was built and discarded every run)
and drops the 32-bit
`x86` target; it uses the runner's preinstalled NDK instead of
downloading one, and drops the
  Android SDK's no-longer-published `tools` package
- A release-mode Tauri config drops the `devtools` feature from release
builds
- A CI-only build gets a SHA-suffixed version instead of silently
reusing the last release's
  version

**Fixes found along the way**
- A release-name typo (`Aventura` → `Aventuras`) that `tauri-action`
would otherwise have kept
  reverting
- The CI version suffix can no longer produce an invalid semver
identifier

## Docs

`docs/development/release.md` substantially rewritten to cover the
shared-workflow architecture,
the release-safety guard, the caching setup, and the build-version
scheme.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added automated desktop and Android builds for Linux, Windows, macOS,
and Android.
- Added signed artifacts, draft releases, prereleases, and
platform-specific downloads.
- Added scheduled and manually triggered builds that create downloadable
artifacts without publishing a release.
- Added macOS Apple Silicon and Linux RPM packages, plus updated Android
and desktop asset naming.
  - Added safeguards to prevent duplicate uploads to published releases.

- **Documentation**
- Updated build prerequisites and documented automated linting,
type-checking, tests, and builds.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 22:38:32 -04:00

82 lines
2.9 KiB
JavaScript

/**
* Version arithmetic for `scripts/release.js`. Split out so it can be tested.
*
* The rules match `semver.inc`. `semver` is not imported: it is only a transitive
* dependency here, and a release script that breaks when an unrelated package drops it is
* worse than forty lines of arithmetic.
*/
/**
* Versions this project can release. Stricter than semver, because a tag is only worth
* making if a workflow builds it: `release.yml` triggers on `v[0-9]+.[0-9]+.[0-9]+` and
* `pre-release.yml` on `v*-pre*`, so a legal `1.2.3-beta.1` would tag, push and build nothing.
*/
const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-pre\.(\d+))?$/
export function isValidVersion(version) {
return typeof version === 'string' && VERSION_PATTERN.test(version)
}
export function parseVersion(version) {
const match = VERSION_PATTERN.exec(version)
if (!match) return null
return {
major: Number(match[1]),
minor: Number(match[2]),
patch: Number(match[3]),
pre: match[4] === undefined ? null : Number(match[4]),
}
}
/** Negative if `a` precedes `b`, positive if it follows, zero if equal. */
export function compareVersions(a, b) {
const x = parseVersion(a)
const y = parseVersion(b)
if (!x || !y) throw new Error(`Cannot compare unparseable versions: "${a}", "${b}"`)
for (const part of ['major', 'minor', 'patch']) {
if (x[part] !== y[part]) return x[part] - y[part]
}
// A pre-release precedes the release it leads to: 0.7.6-pre.1 < 0.7.6.
if (x.pre === null && y.pre === null) return 0
if (x.pre === null) return 1
if (y.pre === null) return -1
return x.pre - y.pre
}
/**
* Apply `type` to `current`: a bump type, or a literal version string.
*
* A pre-release already sitting at the level being bumped is promoted rather than
* incremented — `0.7.6-pre.1` + `patch` is `0.7.6` — because only the suffix stands
* between it and release.
*/
export function bumpVersion(current, type) {
const v = parseVersion(current)
if (!v) throw new Error(`Current version is not a version this project can release: ${current}`)
if (!type) return undefined
const isPre = v.pre !== null
switch (type) {
case 'major':
// Promote only on the major boundary (X.0.0-pre.N).
return isPre && v.minor === 0 && v.patch === 0 ? `${v.major}.0.0` : `${v.major + 1}.0.0`
case 'minor':
return isPre && v.patch === 0 ? `${v.major}.${v.minor}.0` : `${v.major}.${v.minor + 1}.0`
case 'patch':
return isPre ? `${v.major}.${v.minor}.${v.patch}` : `${v.major}.${v.minor}.${v.patch + 1}`
case 'prerelease':
// From a stable version the patch moves first: `0.7.6-pre.1` sorts *before* the
// `0.7.6` already shipped, so an updater would offer users a downgrade.
return isPre
? `${v.major}.${v.minor}.${v.patch}-pre.${v.pre + 1}`
: `${v.major}.${v.minor}.${v.patch + 1}-pre.1`
default:
return type
}
}