mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
The nightly release workflow runs on Linux, but the script was written with BSD-only idioms and had only ever been executed from a Mac. Every scheduled nightly had been failing; the last successful nightly tag was cut manually. - days_since_2026: replace `date -j -f` (BSD) with a python3 one-liner - sed -i: use the portable `sed -i.bak` + rm pattern; empty-suffix `sed -i ''` is a BSD-ism that breaks GNU sed's arg parsing Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
167 lines
4 KiB
Bash
Executable file
167 lines
4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
die() {
|
|
printf '%s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
release_date() {
|
|
printf '%s\n' "${FABRO_RELEASE_DATE:-$(date "+%Y-%m-%d")}"
|
|
}
|
|
|
|
days_since_2026() {
|
|
local target_date="$1"
|
|
python3 -c "from datetime import date; print((date.fromisoformat('$target_date') - date(2026, 1, 1)).days)"
|
|
}
|
|
|
|
next_base_version() {
|
|
local date minor patch
|
|
date="$1"
|
|
minor=$(( $(days_since_2026 "$date") + 100 ))
|
|
patch=0
|
|
|
|
while git rev-parse "v0.${minor}.${patch}" >/dev/null 2>&1; do
|
|
patch=$((patch + 1))
|
|
done
|
|
|
|
printf '0.%s.%s\n' "$minor" "$patch"
|
|
}
|
|
|
|
validate_prerelease_label() {
|
|
local label="$1"
|
|
case "$label" in
|
|
nightly) ;;
|
|
*)
|
|
die "invalid pre-release label: $label (expected: nightly)"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
compute_release_version() {
|
|
local base_version="$1"
|
|
local prerelease_label="$2"
|
|
|
|
if [[ -z "$prerelease_label" ]]; then
|
|
printf '%s\n' "$base_version"
|
|
return 0
|
|
fi
|
|
|
|
local prerelease_number=0
|
|
while git rev-parse "v${base_version}-${prerelease_label}.${prerelease_number}" >/dev/null 2>&1; do
|
|
prerelease_number=$((prerelease_number + 1))
|
|
done
|
|
|
|
printf '%s-%s.%s\n' "$base_version" "$prerelease_label" "$prerelease_number"
|
|
}
|
|
|
|
parse_args() {
|
|
DRY_RUN=0
|
|
SKIP_TESTS=0
|
|
PRERELEASE_LABEL=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
;;
|
|
--skip-tests)
|
|
SKIP_TESTS=1
|
|
;;
|
|
--help|-h)
|
|
cat <<'EOF'
|
|
Usage: bin/dev/release.sh [nightly] [--dry-run] [--skip-tests]
|
|
|
|
Create the next stable release from main, or compute a prerelease tag.
|
|
|
|
Before tagging, runs the same release-mode test smoke the CI release
|
|
workflow does for the native target, with SEGMENT_WRITE_KEY baked in so
|
|
telemetry-active code paths are exercised. This catches regressions
|
|
like "uninstall recreates ~/.fabro via telemetry" or "sender tests
|
|
assume no write key" before we burn a 60-minute CI cycle. Skip only if
|
|
you've already run `cargo nextest run --workspace --release` yourself.
|
|
EOF
|
|
exit 0
|
|
;;
|
|
--*)
|
|
die "unknown option: $1"
|
|
;;
|
|
*)
|
|
if [[ -n "$PRERELEASE_LABEL" ]]; then
|
|
die "expected at most one pre-release label"
|
|
fi
|
|
validate_prerelease_label "$1"
|
|
PRERELEASE_LABEL="$1"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
}
|
|
|
|
verify_spa_assets() {
|
|
local repo_root
|
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
|
|
"${repo_root}/scripts/refresh-fabro-spa.sh"
|
|
|
|
if ! git -C "$repo_root" diff --exit-code -- lib/crates/fabro-spa/assets >/dev/null; then
|
|
die "fabro-spa assets are stale. Commit the refreshed assets before releasing."
|
|
fi
|
|
}
|
|
|
|
verify_release_tests() {
|
|
if [[ "$SKIP_TESTS" == "1" ]]; then
|
|
echo "--skip-tests set, skipping release-mode test smoke"
|
|
return 0
|
|
fi
|
|
|
|
echo "Running release-mode test smoke (SEGMENT_WRITE_KEY baked in)..."
|
|
SEGMENT_WRITE_KEY="fake-for-local-smoke" \
|
|
cargo nextest run \
|
|
--workspace \
|
|
--release \
|
|
--profile ci \
|
|
--status-level slow
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
|
|
verify_spa_assets
|
|
verify_release_tests
|
|
|
|
local repo_root cargo_toml current_version base_version new_version tag
|
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
cargo_toml="${repo_root}/Cargo.toml"
|
|
|
|
current_version=$(grep -m1 '^version = ' "$cargo_toml" | sed 's/version = "\(.*\)"/\1/')
|
|
echo "Current version: $current_version"
|
|
|
|
base_version=$(next_base_version "$(release_date)")
|
|
new_version=$(compute_release_version "$base_version" "$PRERELEASE_LABEL")
|
|
tag="v$new_version"
|
|
|
|
echo "Releasing $new_version (tag $tag)"
|
|
|
|
if [[ "$DRY_RUN" == "1" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
sed -i.bak "s/^version = \"$current_version\"/version = \"$new_version\"/" "$cargo_toml"
|
|
rm "${cargo_toml}.bak"
|
|
echo "Updated $cargo_toml"
|
|
|
|
cargo update --workspace
|
|
echo "Updated Cargo.lock"
|
|
|
|
git add "$cargo_toml" Cargo.lock
|
|
git commit -m "Bump version to $new_version"
|
|
git tag -a "$tag" -m "$tag"
|
|
git push origin main "$tag"
|
|
|
|
echo ""
|
|
echo "Released $tag"
|
|
echo "Watch the build: https://github.com/fabro-sh/fabro/actions"
|
|
}
|
|
|
|
main "$@"
|