Terminal Markdown previewer — GUI-like experience.
1#!/usr/bin/env sh
2set -eu
3
4usage() {
5 echo "Usage: $0 <major|minor|patch> [branch]" >&2
6 echo "Example: $0 patch main" >&2
7 exit 1
8}
9
10BUMPTYPE="${1:-}"
11BRANCH="${2:-main}"
12
13[ -n "$BUMPTYPE" ] || usage
14
15case "$BUMPTYPE" in
16 major | minor | patch)
17 ;;
18 *)
19 echo "Invalid bump type: $BUMPTYPE" >&2
20 usage
21 ;;
22esac
23
24git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
25 echo "Not inside a git repository" >&2
26 exit 1
27}
28
29if ! git diff --quiet; then
30 echo "Working tree has unstaged changes" >&2
31 git status --short >&2
32 exit 1
33fi
34
35if ! git diff --cached --quiet; then
36 echo "Working tree has staged but uncommitted changes" >&2
37 git status --short >&2
38 exit 1
39fi
40
41tmp_file="$(mktemp)"
42trap 'rm -f "$tmp_file"' EXIT
43
44git fetch origin "$BRANCH"
45git fetch --tags origin
46
47if ! git merge-base --is-ancestor "refs/remotes/origin/$BRANCH" HEAD; then
48 echo "Current HEAD is behind or diverged from origin/$BRANCH" >&2
49 exit 1
50fi
51
52CURRENT_VERSION="$(
53 awk '
54 /^\[package\]$/ { in_package = 1; next }
55 /^\[/ && $0 != "[package]" { in_package = 0 }
56 in_package && /^version = "/ {
57 gsub(/^version = "/, "", $0)
58 gsub(/"$/, "", $0)
59 print
60 exit
61 }
62 ' Cargo.toml
63)"
64
65case "$CURRENT_VERSION" in
66 '' | *[!0-9.]* | *.*.*.* | .* | *.)
67 echo "Invalid current version in Cargo.toml: $CURRENT_VERSION" >&2
68 exit 1
69 ;;
70esac
71
72IFS=.
73set -- $CURRENT_VERSION
74IFS=' '
75
76CURRENT_MAJOR="${1:-}"
77CURRENT_MINOR="${2:-}"
78CURRENT_PATCH="${3:-}"
79
80[ -n "$CURRENT_MAJOR" ] || {
81 echo "Invalid current version in Cargo.toml: $CURRENT_VERSION" >&2
82 exit 1
83}
84[ -n "$CURRENT_MINOR" ] || {
85 echo "Invalid current version in Cargo.toml: $CURRENT_VERSION" >&2
86 exit 1
87}
88[ -n "$CURRENT_PATCH" ] || {
89 echo "Invalid current version in Cargo.toml: $CURRENT_VERSION" >&2
90 exit 1
91}
92
93case "$CURRENT_MAJOR:$CURRENT_MINOR:$CURRENT_PATCH" in
94 *[!0-9:]*)
95 echo "Invalid current version in Cargo.toml: $CURRENT_VERSION" >&2
96 exit 1
97 ;;
98esac
99
100case "$BUMPTYPE" in
101 major)
102 NEXT_MAJOR=$((CURRENT_MAJOR + 1))
103 NEXT_MINOR=0
104 NEXT_PATCH=0
105 ;;
106 minor)
107 NEXT_MAJOR=$CURRENT_MAJOR
108 NEXT_MINOR=$((CURRENT_MINOR + 1))
109 NEXT_PATCH=0
110 ;;
111 patch)
112 NEXT_MAJOR=$CURRENT_MAJOR
113 NEXT_MINOR=$CURRENT_MINOR
114 NEXT_PATCH=$((CURRENT_PATCH + 1))
115 ;;
116esac
117
118VERSION="$NEXT_MAJOR.$NEXT_MINOR.$NEXT_PATCH"
119TAG="$VERSION"
120
121if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null 2>&1; then
122 echo "Tag already exists: $TAG" >&2
123 exit 1
124fi
125
126if git ls-remote --tags --exit-code origin "refs/tags/$TAG" >/dev/null 2>&1; then
127 echo "Remote tag already exists on origin: $TAG" >&2
128 exit 1
129fi
130
131awk -v version="$VERSION" '
132 BEGIN {
133 in_package = 0
134 replaced = 0
135 }
136 /^\[package\]$/ {
137 in_package = 1
138 print
139 next
140 }
141 /^\[/ && $0 != "[package]" {
142 in_package = 0
143 }
144 in_package && !replaced && /^version = "/ {
145 print "version = \"" version "\""
146 replaced = 1
147 next
148 }
149 {
150 print
151 }
152 END {
153 if (!replaced) {
154 exit 1
155 }
156 }
157' Cargo.toml > "$tmp_file"
158mv "$tmp_file" Cargo.toml
159
160if git diff --quiet -- Cargo.toml; then
161 echo "Cargo.toml already set to version $VERSION" >&2
162 exit 1
163fi
164
165cargo update --workspace >/dev/null
166
167PREV_TAG="$(
168 git tag --sort=-v:refname \
169 | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \
170 | head -n 1
171)"
172[ -n "$PREV_TAG" ] || {
173 echo "No previous semver tag found" >&2
174 exit 1
175}
176
177./scripts/changelog-update.sh "$PREV_TAG" "$TAG"
178
179git add Cargo.toml Cargo.lock CHANGELOG.md
180git commit -m "$VERSION"
181git tag "$TAG"
182git push origin "HEAD:$BRANCH"
183git push origin "$TAG"