mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
chore(ci): Move release process to GitHub Actions
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env bats
|
||||
# Unit tests for bump.sh. Run with: bats tools/release/bump.bats
|
||||
|
||||
setup() {
|
||||
BUMP_SH="${BATS_TEST_DIRNAME}/bump.sh"
|
||||
TMP_REPO="$(mktemp -d)"
|
||||
cat > "$TMP_REPO/version.properties" <<'EOF'
|
||||
### Updated by release.sh ###
|
||||
project.versioning.major=5
|
||||
project.versioning.minor=1
|
||||
project.versioning.patch=1
|
||||
project.versioning.build=0
|
||||
project.versioning.type=rc
|
||||
#############################
|
||||
EOF
|
||||
echo "5.1.1-rc0 50101000" > "$TMP_REPO/VERSION"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
rm -rf "$TMP_REPO"
|
||||
}
|
||||
|
||||
bump() {
|
||||
"$BUMP_SH" --repo-root="$TMP_REPO" "$@"
|
||||
}
|
||||
|
||||
# ----- check mode ----------------------------------------------------------
|
||||
|
||||
@test "check: passes on consistent state" {
|
||||
run bump --mode=check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"current_name=5.1.1-rc0"* ]]
|
||||
[[ "$output" == *"current_code=50101000"* ]]
|
||||
}
|
||||
|
||||
@test "check: fails on VERSION/version.properties drift (name)" {
|
||||
echo "5.1.0-rc0 50100000" > "$TMP_REPO/VERSION"
|
||||
run bump --mode=check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"drift"* ]]
|
||||
}
|
||||
|
||||
@test "check: fails on VERSION/version.properties drift (code)" {
|
||||
echo "5.1.1-rc0 99999999" > "$TMP_REPO/VERSION"
|
||||
run bump --mode=check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"drift"* ]]
|
||||
}
|
||||
|
||||
@test "check: fails on duplicate key" {
|
||||
echo "project.versioning.major=6" >> "$TMP_REPO/version.properties"
|
||||
run bump --mode=check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"major"* ]]
|
||||
[[ "$output" == *"found 2"* ]]
|
||||
}
|
||||
|
||||
@test "check: fails on missing key" {
|
||||
sed -i '/project\.versioning\.type=/d' "$TMP_REPO/version.properties"
|
||||
run bump --mode=check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"type"* ]]
|
||||
[[ "$output" == *"found 0"* ]]
|
||||
}
|
||||
|
||||
@test "check: fails on bad type" {
|
||||
sed -i 's/^project\.versioning\.type=.*/project.versioning.type=foo/' "$TMP_REPO/version.properties"
|
||||
run bump --mode=check
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "check: fails on malformed VERSION" {
|
||||
echo "garbage" > "$TMP_REPO/VERSION"
|
||||
run bump --mode=check
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"VERSION file does not match"* ]]
|
||||
}
|
||||
|
||||
# ----- plan: bump kinds ----------------------------------------------------
|
||||
|
||||
@test "plan: build bump increments build" {
|
||||
run bump --mode=plan --bump-kind=build --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=5.1.1-rc1"* ]]
|
||||
[[ "$output" == *"new_code=50101010"* ]]
|
||||
}
|
||||
|
||||
@test "plan: patch bump zeros build" {
|
||||
run bump --mode=plan --bump-kind=patch --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=5.1.2-rc0"* ]]
|
||||
[[ "$output" == *"new_code=50102000"* ]]
|
||||
}
|
||||
|
||||
@test "plan: minor bump zeros patch and build" {
|
||||
run bump --mode=plan --bump-kind=minor --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=5.2.0-rc0"* ]]
|
||||
[[ "$output" == *"new_code=50200000"* ]]
|
||||
}
|
||||
|
||||
@test "plan: major bump zeros minor, patch, build" {
|
||||
run bump --mode=plan --bump-kind=major --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=6.0.0-rc0"* ]]
|
||||
[[ "$output" == *"new_code=60000000"* ]]
|
||||
}
|
||||
|
||||
@test "plan: version-type beta switches type" {
|
||||
run bump --mode=plan --bump-kind=build --version-type=beta
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=5.1.1-beta1"* ]]
|
||||
}
|
||||
|
||||
@test "plan: keep-current preserves type" {
|
||||
# change current type to beta in fixture
|
||||
sed -i 's/^project\.versioning\.type=.*/project.versioning.type=beta/' "$TMP_REPO/version.properties"
|
||||
echo "5.1.1-beta0 50101000" > "$TMP_REPO/VERSION"
|
||||
run bump --mode=plan --bump-kind=build --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=5.1.1-beta1"* ]]
|
||||
}
|
||||
|
||||
# ----- plan: override ------------------------------------------------------
|
||||
|
||||
@test "plan: version-override accepts valid version" {
|
||||
run bump --mode=plan --version-override=5.2.0-rc0
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"new_name=5.2.0-rc0"* ]]
|
||||
[[ "$output" == *"new_code=50200000"* ]]
|
||||
}
|
||||
|
||||
@test "plan: version-override rejects bad regex (build=100)" {
|
||||
run bump --mode=plan --version-override=5.1.2-rc100
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"does not match"* ]]
|
||||
}
|
||||
|
||||
@test "plan: version-override rejects bad type" {
|
||||
run bump --mode=plan --version-override=5.1.2-alpha0
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "plan: version-override rejects leading zero" {
|
||||
run bump --mode=plan --version-override=5.01.0-rc0
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"leading zero"* ]]
|
||||
}
|
||||
|
||||
@test "plan: version-override rejects no-op identity" {
|
||||
run bump --mode=plan --version-override=5.1.1-rc0
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no-op"* ]]
|
||||
}
|
||||
|
||||
@test "plan: version-override rejects monotonicity break" {
|
||||
run bump --mode=plan --version-override=5.1.0-rc0
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"monotonicity"* ]]
|
||||
}
|
||||
|
||||
# ----- plan: bounds --------------------------------------------------------
|
||||
|
||||
@test "plan: rejects build overflow when bumping past 99" {
|
||||
sed -i 's/^project\.versioning\.build=.*/project.versioning.build=99/' "$TMP_REPO/version.properties"
|
||||
echo "5.1.1-rc99 50101990" > "$TMP_REPO/VERSION"
|
||||
run bump --mode=plan --bump-kind=build --version-type=keep-current
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"out of range"* ]]
|
||||
}
|
||||
|
||||
@test "plan: rejects patch overflow when bumping past 99" {
|
||||
sed -i 's/^project\.versioning\.patch=.*/project.versioning.patch=99/' "$TMP_REPO/version.properties"
|
||||
echo "5.1.99-rc0 50199000" > "$TMP_REPO/VERSION"
|
||||
run bump --mode=plan --bump-kind=patch --version-type=keep-current
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"out of range"* ]]
|
||||
}
|
||||
|
||||
# ----- plan: expected-current ----------------------------------------------
|
||||
|
||||
@test "plan: expected-current matches passes" {
|
||||
run bump --mode=plan --bump-kind=build --version-type=keep-current --expected-current=5.1.1-rc0
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "plan: expected-current mismatch fails" {
|
||||
run bump --mode=plan --bump-kind=build --version-type=keep-current --expected-current=5.0.0-rc0
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"expected-current"* ]]
|
||||
}
|
||||
|
||||
# ----- write mode ----------------------------------------------------------
|
||||
|
||||
@test "write: rewrites both files correctly" {
|
||||
run bump --mode=write --bump-kind=build --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
grep -q '^project\.versioning\.build=1$' "$TMP_REPO/version.properties"
|
||||
[ "$(cat "$TMP_REPO/VERSION")" = "5.1.1-rc1 50101010" ]
|
||||
}
|
||||
|
||||
@test "write: preserves comment block" {
|
||||
run bump --mode=write --bump-kind=patch --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
grep -q '^### Updated by tools/release/bump.sh ###$' "$TMP_REPO/version.properties"
|
||||
grep -q '^#############################$' "$TMP_REPO/version.properties"
|
||||
}
|
||||
|
||||
@test "write: preserves key order" {
|
||||
run bump --mode=write --bump-kind=patch --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
grep -nE '^project\.versioning\.' "$TMP_REPO/version.properties" > "$TMP_REPO/order.txt"
|
||||
# Five keys, in order: major, minor, patch, build, type.
|
||||
run cat "$TMP_REPO/order.txt"
|
||||
[[ "${lines[0]}" == *"major="* ]]
|
||||
[[ "${lines[1]}" == *"minor="* ]]
|
||||
[[ "${lines[2]}" == *"patch="* ]]
|
||||
[[ "${lines[3]}" == *"build="* ]]
|
||||
[[ "${lines[4]}" == *"type="* ]]
|
||||
}
|
||||
|
||||
@test "write: type switch persists" {
|
||||
run bump --mode=write --bump-kind=build --version-type=beta
|
||||
[ "$status" -eq 0 ]
|
||||
grep -q '^project\.versioning\.type=beta$' "$TMP_REPO/version.properties"
|
||||
[ "$(cat "$TMP_REPO/VERSION")" = "5.1.1-beta1 50101010" ]
|
||||
}
|
||||
|
||||
@test "write: idempotent re-check passes" {
|
||||
run bump --mode=write --bump-kind=build --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
run bump --mode=check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"current_name=5.1.1-rc1"* ]]
|
||||
}
|
||||
|
||||
@test "write: header refresh idempotent if already updated" {
|
||||
sed -i 's|^### Updated by release\.sh ###$|### Updated by tools/release/bump.sh ###|' "$TMP_REPO/version.properties"
|
||||
run bump --mode=write --bump-kind=build --version-type=keep-current
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(grep -c '^### Updated by tools/release/bump.sh ###$' "$TMP_REPO/version.properties")" -eq 1 ]
|
||||
}
|
||||
|
||||
# ----- mode parsing --------------------------------------------------------
|
||||
|
||||
@test "rejects missing --mode" {
|
||||
run "$BUMP_SH" --repo-root="$TMP_REPO"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "rejects invalid --mode" {
|
||||
run "$BUMP_SH" --repo-root="$TMP_REPO" --mode=foo
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "rejects unknown flag" {
|
||||
run "$BUMP_SH" --repo-root="$TMP_REPO" --mode=check --frobnicate=yes
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
Executable
+332
@@ -0,0 +1,332 @@
|
||||
#!/usr/bin/env bash
|
||||
# Source of truth for version bumping. Used by release-prepare.yml and release-tag.yml.
|
||||
# Mirrors the versionCode formula in buildSrc/src/main/java/ProjectConfig.kt.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: bump.sh --mode=<check|plan|write> [options]
|
||||
|
||||
Modes:
|
||||
check Validate version.properties + VERSION are consistent. No mutation.
|
||||
plan check + compute the new version per inputs. Print plan to stdout.
|
||||
write plan + rewrite version.properties and VERSION, verify post-condition.
|
||||
|
||||
Options (for plan/write):
|
||||
--bump-kind=build|patch|minor|major (default: build)
|
||||
--version-type=keep-current|rc|beta (default: keep-current)
|
||||
--version-override=<M.m.p-(rc|beta)n> (overrides bump-kind/version-type)
|
||||
--expected-current=<M.m.p-(rc|beta)n> (fail if current version differs)
|
||||
--repo-root=<path> (default: current working directory)
|
||||
|
||||
Output (plan/write modes):
|
||||
Human-readable report on stderr; KEY=value pairs on stdout for parsing:
|
||||
current_name=...
|
||||
current_code=...
|
||||
new_name=...
|
||||
new_code=...
|
||||
EOF
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
echo "$*" >&2
|
||||
}
|
||||
|
||||
# ----- argument parsing ----------------------------------------------------
|
||||
|
||||
mode=""
|
||||
bump_kind="build"
|
||||
version_type="keep-current"
|
||||
version_override=""
|
||||
expected_current=""
|
||||
repo_root=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--mode=*) mode="${arg#*=}" ;;
|
||||
--bump-kind=*) bump_kind="${arg#*=}" ;;
|
||||
--version-type=*) version_type="${arg#*=}" ;;
|
||||
--version-override=*) version_override="${arg#*=}" ;;
|
||||
--expected-current=*) expected_current="${arg#*=}" ;;
|
||||
--repo-root=*) repo_root="${arg#*=}" ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$mode" in
|
||||
check|plan|write) ;;
|
||||
"") usage >&2; exit 2 ;;
|
||||
*) die "invalid --mode: $mode (expected check|plan|write)" ;;
|
||||
esac
|
||||
|
||||
case "$bump_kind" in
|
||||
build|patch|minor|major) ;;
|
||||
*) die "invalid --bump-kind: $bump_kind" ;;
|
||||
esac
|
||||
|
||||
case "$version_type" in
|
||||
keep-current|rc|beta) ;;
|
||||
*) die "invalid --version-type: $version_type" ;;
|
||||
esac
|
||||
|
||||
if [[ -z "$repo_root" ]]; then
|
||||
repo_root="$(pwd)"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$repo_root" ]]; then
|
||||
die "repo root does not exist: $repo_root"
|
||||
fi
|
||||
|
||||
props_file="$repo_root/version.properties"
|
||||
version_file="$repo_root/VERSION"
|
||||
|
||||
# ----- helpers -------------------------------------------------------------
|
||||
|
||||
# Reject leading zeros on numeric components (allow plain "0").
|
||||
no_leading_zero() {
|
||||
local n="$1" label="$2"
|
||||
[[ "$n" =~ ^0$ || "$n" =~ ^[1-9][0-9]*$ ]] || die "$label has leading zero or invalid digits: '$n'"
|
||||
}
|
||||
|
||||
bound_0_99() {
|
||||
local n="$1" label="$2"
|
||||
[[ "$n" -ge 0 && "$n" -le 99 ]] || die "$label out of range 0..99: $n"
|
||||
}
|
||||
|
||||
parse_name() {
|
||||
# Sets globals: pn_major, pn_minor, pn_patch, pn_type, pn_build
|
||||
local name="$1" label="$2"
|
||||
if [[ ! "$name" =~ ^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,2})-(rc|beta)([0-9]{1,2})$ ]]; then
|
||||
die "$label does not match <M.m.p-(rc|beta)n>: '$name'"
|
||||
fi
|
||||
pn_major="${BASH_REMATCH[1]}"
|
||||
pn_minor="${BASH_REMATCH[2]}"
|
||||
pn_patch="${BASH_REMATCH[3]}"
|
||||
pn_type="${BASH_REMATCH[4]}"
|
||||
pn_build="${BASH_REMATCH[5]}"
|
||||
no_leading_zero "$pn_major" "$label major"
|
||||
no_leading_zero "$pn_minor" "$label minor"
|
||||
no_leading_zero "$pn_patch" "$label patch"
|
||||
no_leading_zero "$pn_build" "$label build"
|
||||
}
|
||||
|
||||
# Compute versionCode the same way ProjectConfig.kt does.
|
||||
compute_code() {
|
||||
local major="$1" minor="$2" patch="$3" build="$4"
|
||||
echo $(( major * 10000000 + minor * 100000 + patch * 1000 + build * 10 ))
|
||||
}
|
||||
|
||||
format_name() {
|
||||
local major="$1" minor="$2" patch="$3" type="$4" build="$5"
|
||||
echo "${major}.${minor}.${patch}-${type}${build}"
|
||||
}
|
||||
|
||||
# Count exact matches of an anchored regex in a file.
|
||||
count_matches() {
|
||||
local pattern="$1" file="$2"
|
||||
grep -cE "$pattern" "$file" || true
|
||||
}
|
||||
|
||||
# ----- read & validate current state ---------------------------------------
|
||||
|
||||
[[ -f "$props_file" ]] || die "missing version.properties at $props_file"
|
||||
[[ -f "$version_file" ]] || die "missing VERSION file at $version_file"
|
||||
|
||||
# Each of the five keys must appear exactly once on its own line.
|
||||
expect_one() {
|
||||
local key_pattern="$1" label="$2" file="$3"
|
||||
local n
|
||||
n=$(count_matches "$key_pattern" "$file")
|
||||
[[ "$n" == "1" ]] || die "$label: expected exactly 1 line in $file matching '$key_pattern', found $n"
|
||||
}
|
||||
|
||||
expect_one '^project\.versioning\.major=[0-9]+$' "major" "$props_file"
|
||||
expect_one '^project\.versioning\.minor=[0-9]+$' "minor" "$props_file"
|
||||
expect_one '^project\.versioning\.patch=[0-9]+$' "patch" "$props_file"
|
||||
expect_one '^project\.versioning\.build=[0-9]+$' "build" "$props_file"
|
||||
expect_one '^project\.versioning\.type=(rc|beta)$' "type" "$props_file"
|
||||
|
||||
cur_major=$(grep -E '^project\.versioning\.major=' "$props_file" | cut -d= -f2)
|
||||
cur_minor=$(grep -E '^project\.versioning\.minor=' "$props_file" | cut -d= -f2)
|
||||
cur_patch=$(grep -E '^project\.versioning\.patch=' "$props_file" | cut -d= -f2)
|
||||
cur_build=$(grep -E '^project\.versioning\.build=' "$props_file" | cut -d= -f2)
|
||||
cur_type=$(grep -E '^project\.versioning\.type=' "$props_file" | cut -d= -f2)
|
||||
|
||||
no_leading_zero "$cur_major" "current major"
|
||||
no_leading_zero "$cur_minor" "current minor"
|
||||
no_leading_zero "$cur_patch" "current patch"
|
||||
no_leading_zero "$cur_build" "current build"
|
||||
bound_0_99 "$cur_major" "current major"
|
||||
bound_0_99 "$cur_minor" "current minor"
|
||||
bound_0_99 "$cur_patch" "current patch"
|
||||
bound_0_99 "$cur_build" "current build"
|
||||
|
||||
cur_name=$(format_name "$cur_major" "$cur_minor" "$cur_patch" "$cur_type" "$cur_build")
|
||||
cur_code=$(compute_code "$cur_major" "$cur_minor" "$cur_patch" "$cur_build")
|
||||
|
||||
# VERSION file: exactly one line, "<name> <code>".
|
||||
version_line_count=$(wc -l < "$version_file" | tr -d ' ')
|
||||
# Allow trailing newline (line count 1) — reject anything else.
|
||||
[[ "$version_line_count" -ge 1 && "$version_line_count" -le 1 ]] \
|
||||
|| die "VERSION file must have exactly one line, found $version_line_count"
|
||||
|
||||
version_content=$(head -n1 "$version_file")
|
||||
if [[ ! "$version_content" =~ ^([^[:space:]]+)\ ([0-9]+)$ ]]; then
|
||||
die "VERSION file does not match '<name> <code>': '$version_content'"
|
||||
fi
|
||||
file_name="${BASH_REMATCH[1]}"
|
||||
file_code="${BASH_REMATCH[2]}"
|
||||
|
||||
# Drift check: VERSION must agree with version.properties.
|
||||
[[ "$file_name" == "$cur_name" ]] \
|
||||
|| die "drift: VERSION name '$file_name' != version.properties name '$cur_name'"
|
||||
[[ "$file_code" == "$cur_code" ]] \
|
||||
|| die "drift: VERSION code '$file_code' != computed code '$cur_code'"
|
||||
|
||||
if [[ -n "$expected_current" ]]; then
|
||||
[[ "$expected_current" == "$cur_name" ]] \
|
||||
|| die "expected-current '$expected_current' != actual current '$cur_name'"
|
||||
fi
|
||||
|
||||
log "current: $cur_name (code $cur_code)"
|
||||
|
||||
if [[ "$mode" == "check" ]]; then
|
||||
echo "current_name=$cur_name"
|
||||
echo "current_code=$cur_code"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ----- compute new version -------------------------------------------------
|
||||
|
||||
if [[ -n "$version_override" ]]; then
|
||||
parse_name "$version_override" "version-override"
|
||||
new_major="$pn_major"
|
||||
new_minor="$pn_minor"
|
||||
new_patch="$pn_patch"
|
||||
new_type="$pn_type"
|
||||
new_build="$pn_build"
|
||||
else
|
||||
new_major="$cur_major"
|
||||
new_minor="$cur_minor"
|
||||
new_patch="$cur_patch"
|
||||
new_build="$cur_build"
|
||||
|
||||
case "$bump_kind" in
|
||||
build) new_build=$((cur_build + 1)) ;;
|
||||
patch) new_patch=$((cur_patch + 1)); new_build=0 ;;
|
||||
minor) new_minor=$((cur_minor + 1)); new_patch=0; new_build=0 ;;
|
||||
major) new_major=$((cur_major + 1)); new_minor=0; new_patch=0; new_build=0 ;;
|
||||
esac
|
||||
|
||||
case "$version_type" in
|
||||
keep-current) new_type="$cur_type" ;;
|
||||
rc|beta) new_type="$version_type" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
bound_0_99 "$new_major" "new major"
|
||||
bound_0_99 "$new_minor" "new minor"
|
||||
bound_0_99 "$new_patch" "new patch"
|
||||
bound_0_99 "$new_build" "new build"
|
||||
|
||||
case "$new_type" in
|
||||
rc|beta) ;;
|
||||
*) die "computed type must be rc or beta, got '$new_type'" ;;
|
||||
esac
|
||||
|
||||
new_name=$(format_name "$new_major" "$new_minor" "$new_patch" "$new_type" "$new_build")
|
||||
new_code=$(compute_code "$new_major" "$new_minor" "$new_patch" "$new_build")
|
||||
|
||||
# Sanity: int range. Android versionCode is Int (max 2_147_483_647).
|
||||
# At major=99 the code is ~990M, well within range — keep the assertion anyway.
|
||||
[[ "$new_code" -le 2147483647 ]] || die "new versionCode exceeds Int.MAX_VALUE: $new_code"
|
||||
|
||||
[[ "$new_name" != "$cur_name" ]] || die "no-op: new name equals current ($new_name)"
|
||||
[[ "$new_code" -gt "$cur_code" ]] \
|
||||
|| die "monotonicity: new code ($new_code) is not greater than current ($cur_code)"
|
||||
|
||||
log "new: $new_name (code $new_code)"
|
||||
|
||||
# ----- output --------------------------------------------------------------
|
||||
|
||||
echo "current_name=$cur_name"
|
||||
echo "current_code=$cur_code"
|
||||
echo "new_name=$new_name"
|
||||
echo "new_code=$new_code"
|
||||
|
||||
if [[ "$mode" == "plan" ]]; then
|
||||
log ""
|
||||
log "--- diff (plan) ---"
|
||||
log " version.properties:"
|
||||
log " major: $cur_major -> $new_major"
|
||||
log " minor: $cur_minor -> $new_minor"
|
||||
log " patch: $cur_patch -> $new_patch"
|
||||
log " build: $cur_build -> $new_build"
|
||||
log " type: $cur_type -> $new_type"
|
||||
log " VERSION:"
|
||||
log " -$cur_name $cur_code"
|
||||
log " +$new_name $new_code"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ----- write mode ----------------------------------------------------------
|
||||
|
||||
# Use sed -E with anchored patterns. Replace each property line in place.
|
||||
# sed exits 0 even if a pattern doesn't match — we count matches before and
|
||||
# verify by re-parsing after.
|
||||
|
||||
sed_inplace() {
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
sed -i '' -E "$@"
|
||||
else
|
||||
sed -i -E "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
sed_inplace "s#^(project\.versioning\.major=)[0-9]+\$#\1${new_major}#" "$props_file"
|
||||
sed_inplace "s#^(project\.versioning\.minor=)[0-9]+\$#\1${new_minor}#" "$props_file"
|
||||
sed_inplace "s#^(project\.versioning\.patch=)[0-9]+\$#\1${new_patch}#" "$props_file"
|
||||
sed_inplace "s#^(project\.versioning\.build=)[0-9]+\$#\1${new_build}#" "$props_file"
|
||||
sed_inplace "s#^(project\.versioning\.type=)(rc|beta)\$#\1${new_type}#" "$props_file"
|
||||
|
||||
# Header comment refresh (idempotent — only updates if it still says the old text).
|
||||
sed_inplace "s@^### Updated by release\.sh ###\$@### Updated by tools/release/bump.sh ###@" "$props_file"
|
||||
|
||||
# Rewrite VERSION (single-line file).
|
||||
printf '%s %s\n' "$new_name" "$new_code" > "$version_file"
|
||||
|
||||
# ----- post-condition ------------------------------------------------------
|
||||
|
||||
# Re-read and verify the rewrite landed exactly as planned.
|
||||
post_major=$(grep -E '^project\.versioning\.major=' "$props_file" | cut -d= -f2)
|
||||
post_minor=$(grep -E '^project\.versioning\.minor=' "$props_file" | cut -d= -f2)
|
||||
post_patch=$(grep -E '^project\.versioning\.patch=' "$props_file" | cut -d= -f2)
|
||||
post_build=$(grep -E '^project\.versioning\.build=' "$props_file" | cut -d= -f2)
|
||||
post_type=$(grep -E '^project\.versioning\.type=' "$props_file" | cut -d= -f2)
|
||||
|
||||
[[ "$post_major" == "$new_major" ]] || die "post-condition: major did not write ($post_major != $new_major)"
|
||||
[[ "$post_minor" == "$new_minor" ]] || die "post-condition: minor did not write ($post_minor != $new_minor)"
|
||||
[[ "$post_patch" == "$new_patch" ]] || die "post-condition: patch did not write ($post_patch != $new_patch)"
|
||||
[[ "$post_build" == "$new_build" ]] || die "post-condition: build did not write ($post_build != $new_build)"
|
||||
[[ "$post_type" == "$new_type" ]] || die "post-condition: type did not write ($post_type != $new_type)"
|
||||
|
||||
# Each key still appears exactly once.
|
||||
expect_one '^project\.versioning\.major=[0-9]+$' "post major" "$props_file"
|
||||
expect_one '^project\.versioning\.minor=[0-9]+$' "post minor" "$props_file"
|
||||
expect_one '^project\.versioning\.patch=[0-9]+$' "post patch" "$props_file"
|
||||
expect_one '^project\.versioning\.build=[0-9]+$' "post build" "$props_file"
|
||||
expect_one '^project\.versioning\.type=(rc|beta)$' "post type" "$props_file"
|
||||
|
||||
# VERSION re-parse.
|
||||
post_version_content=$(head -n1 "$version_file")
|
||||
[[ "$post_version_content" == "$new_name $new_code" ]] \
|
||||
|| die "post-condition: VERSION did not write ('$post_version_content' != '$new_name $new_code')"
|
||||
|
||||
log "wrote: $new_name (code $new_code)"
|
||||
Reference in New Issue
Block a user