-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrelease.sh
More file actions
executable file
·110 lines (90 loc) · 3.57 KB
/
Copy pathrelease.sh
File metadata and controls
executable file
·110 lines (90 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env bash
# Cut a new release of the polylane CLI.
#
# ./release.sh 0.1.0
# ./release.sh # prompts for version (interactive only);
# # empty answer = next patch version
#
# Steps: bump package.json → typecheck + lint + test + build → commit → tag →
# push. `release.yml` takes over once the v* tag reaches origin.
#
# CI mode (CI env var set, as in GitHub Actions): no prompts — no version
# argument means "bump the next patch version" and the branch must be main.
# Run via the "Cut Release" workflow (.github/workflows/cut-release.yml)
# instead of a laptop.
#
# RELEASE_DRY_RUN=1 runs the version bump and all checks, then reverts the
# bump and exits before committing, tagging, or pushing.
set -euo pipefail
die() { printf '\033[31merror\033[0m: %s\n' "$*" >&2; exit 1; }
info() { printf '\033[2m→ %s\033[0m\n' "$*"; }
ok() { printf '\033[32m✓\033[0m %s\n' "$*"; }
cd "$(dirname "$0")"
[ -f package.json ] || die "run from the repo root (package.json not found here)"
# Clean tree — version bump and tag must land on a known state.
if [ -n "$(git status --porcelain)" ]; then
git status --short >&2
die "working tree is dirty — commit or stash first"
fi
# Version: arg or prompt (never prompt in CI). Empty means "next patch".
VERSION="${1:-}"
if [ -z "$VERSION" ] && [ -z "${CI:-}" ]; then
read -rp "Release version (no v prefix, e.g. 0.1.0; empty = next patch): " VERSION
fi
VERSION="${VERSION#v}"
# No version given: compute the next patch from package.json, matching what
# `npm version patch` would produce (a prerelease is promoted to its release).
if [ -z "$VERSION" ]; then
CURRENT="$(node -p 'require("./package.json").version')"
VERSION="$(node -p '
const m = process.argv[1].match(/^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]*)?/);
m[4] ? `${m[1]}.${m[2]}.${m[3]}` : `${m[1]}.${m[2]}.${Number(m[3]) + 1}`
' "$CURRENT")" || die "could not compute next patch version from '$CURRENT'"
info "no version given — next patch: $CURRENT → $VERSION"
fi
# Reject anything that isn't semver x.y.z[-prerelease][+build].
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
die "invalid semver: '$VERSION' (expected x.y.z)"
fi
TAG="v$VERSION"
if git rev-parse "$TAG" >/dev/null 2>&1; then
die "tag $TAG already exists locally"
fi
if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then
die "tag $TAG already exists on origin"
fi
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
if [ "$BRANCH" != "main" ]; then
if [ -n "${CI:-}" ]; then
die "refusing to release from '$BRANCH' in CI — releases are cut from main"
fi
read -rp "Current branch is '$BRANCH', not 'main'. Continue? [y/N] " ans
[[ "$ans" =~ ^[Yy]$ ]] || die "aborted"
fi
info "bumping package.json to $VERSION"
npm version "$VERSION" --no-git-tag-version --allow-same-version >/dev/null
ok "package.json → $VERSION"
info "typecheck"
npm run typecheck
info "lint"
npm run lint
info "test"
npm run test
info "build"
npm run build
ok "all checks passed"
if [ -n "${RELEASE_DRY_RUN:-}" ]; then
info "dry run — reverting version bump; skipping commit, tag, and push"
git checkout -- package.json package-lock.json
ok "Dry run for $TAG passed all checks"
exit 0
fi
info "committing version bump"
git add package.json package-lock.json
git commit -m "chore: release $TAG"
info "tagging $TAG"
git tag -a "$TAG" -m "polylane $VERSION"
info "pushing $BRANCH + $TAG to origin"
git push origin "$BRANCH"
git push origin "$TAG"
ok "Released $TAG — watch: https://github.com/coreplanelabs/cli/actions"