| @@ -6,23 +6,40 @@ |
| 6 | 6 | # appimagetool is used from $PATH if present, otherwise downloaded once into |
| 7 | 7 | # build/.tools. Set APPIMAGETOOL=/path/to/appimagetool to use a specific one. |
| 8 | 8 | # |
| 9 | | - # ./build.sh --release (or -r) then publishes them as a release on Vectrum: |
| 10 | | - # it tags the commit with the version in Cargo.toml, pushes the tag, and |
| 11 | | - # uploads the AppImage, the binary and SHA256SUMS (the first upload |
| 12 | | - # publishes the release). Bump the version in Cargo.toml for each release. |
| 9 | + # Options: |
| 10 | + # -v, --version X set the version to X (0.1.2) first: updates Cargo.toml |
| 11 | + # and Cargo.lock and commits them as "Version X" |
| 12 | + # -r, --release then publish the build as a release on Vectrum: tag the |
| 13 | + # commit with the version, push the tag, and upload the |
| 14 | + # AppImage, the binary and SHA256SUMS (the first upload |
| 15 | + # publishes the release). Each release needs a new version. |
| 16 | + # -h, --help show this |
| 17 | + # |
| 18 | + # So ./build.sh -v 0.1.2 -r makes and publishes version 0.1.2. Releasing reads: |
| 13 | 19 | # VECTRUM_TOKEN a read & write API token (vec_…); otherwise read from |
| 14 | 20 | # ~/.config/vectrum/token (or $VECTRUM_TOKEN_FILE) |
| 15 | 21 | # VECTRUM_URL the server, https://vectrum.cc by default |
| 16 | 22 | # VECTRUM_REPO owner/repo, taken from the origin remote by default |
| 17 | 23 | set -euo pipefail |
| 18 | 24 | |
| 25 | + log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } |
| 26 | + die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; } |
| 27 | + |
| 19 | 28 | RELEASE=0 |
| 20 | | - for arg in "$@"; do |
| 21 | | - case "$arg" in |
| 29 | + NEW_VERSION="" |
| 30 | + while (( $# )); do |
| 31 | + case "$1" in |
| 22 | 32 | -r|--release) RELEASE=1 ;; |
| 33 | + -v|--version) |
| 34 | + [[ $# -ge 2 ]] || die "$1 needs a version, like $1 0.1.2" |
| 35 | + NEW_VERSION="$2" |
| 36 | + shift |
| 37 | + ;; |
| 38 | + --version=*) NEW_VERSION="${1#*=}" ;; |
| 23 | 39 | -h|--help) sed -n '2,/^set -euo/p' "$0" | sed '$d; s/^# \{0,1\}//'; exit 0 ;; |
| 24 | | - *) printf 'Unknown option: %s (try --help)\n' "$arg" >&2; exit 2 ;; |
| 40 | + *) printf 'Unknown option: %s (try --help)\n' "$1" >&2; exit 2 ;; |
| 25 | 41 | esac |
| 42 | + shift |
| 26 | 43 | done |
| 27 | 44 | |
| 28 | 45 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| @@ -35,12 +52,21 @@ PRETTY="Vectrum Code" |
| 35 | 52 | VERSION="$(sed -n 's/^version *= *"\(.*\)"/\1/p' "$ROOT/Cargo.toml" | head -n1)" |
| 36 | 53 | ARCH="$(uname -m)" |
| 37 | 54 | |
| 38 | | - log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } |
| 39 | | - die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; } |
| 55 | + dirty() { [[ -n "$(git -C "$ROOT" status --porcelain)" ]]; } |
| 40 | 56 | |
| 41 | | - # ── 0. Release checks, before spending time on a build ─────────────────────── |
| 57 | + # Stops if tag $1 already names a commit other than $2 (any commit, if $2 is |
| 58 | + # empty), here or on origin. Sets REMOTE_TAG and LOCAL_TAG. |
| 59 | + check_tag() { |
| 60 | + REMOTE_TAG="$(git -C "$ROOT" ls-remote origin "refs/tags/$1^{}" "refs/tags/$1" | awk 'NR==1 || /\^\{\}$/ {sha=$1} END {print sha}')" |
| 61 | + LOCAL_TAG="$(git -C "$ROOT" rev-parse -q --verify "refs/tags/$1^{commit}" || true)" |
| 62 | + for tagged in "$REMOTE_TAG" "$LOCAL_TAG"; do |
| 63 | + [[ -z "$tagged" || "$tagged" == "$2" ]] || |
| 64 | + die "$1 is already tagged at ${tagged:0:7}; pick a new version with -v" |
| 65 | + done |
| 66 | + } |
| 67 | + |
| 68 | + # ── 0. Version and release checks, before spending time on a build ─────────── |
| 42 | 69 | if (( RELEASE )); then |
| 43 | | - TAG="$VERSION" |
| 44 | 70 | SERVER="${VECTRUM_URL:-https://vectrum.cc}" |
| 45 | 71 | SERVER="${SERVER%/}" |
| 46 | 72 | TOKEN_FILE="${VECTRUM_TOKEN_FILE:-$HOME/.config/vectrum/token}" |
| @@ -52,14 +78,27 @@ if (( RELEASE )); then |
| 52 | 78 | # ssh://git@git.vectrum.cc:2222/vectrum/code.git, https://…/vectrum/code → vectrum/code |
| 53 | 79 | REPO="${VECTRUM_REPO:-$(git -C "$ROOT" remote get-url origin | sed -E 's#\.git$##; s#^.*[:/]([^/:]+/[^/:]+)$#\1#')}" |
| 54 | 80 | [[ "$REPO" == */* ]] || die "Couldn't tell the repository from the origin remote; set VECTRUM_REPO=owner/repo" |
| 55 | | - [[ -z "$(git -C "$ROOT" status --porcelain)" ]] || die "Commit or stash your changes first: a release is built from a commit" |
| 81 | + fi |
| 82 | + |
| 83 | + if [[ -n "$NEW_VERSION" && "$NEW_VERSION" != "$VERSION" ]]; then |
| 84 | + [[ "$NEW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]] || |
| 85 | + die "$NEW_VERSION isn't a version like 0.1.2" |
| 86 | + ! dirty || die "Commit or stash your changes first: the new version is committed on its own" |
| 87 | + if (( RELEASE )); then |
| 88 | + check_tag "$NEW_VERSION" "" |
| 89 | + fi |
| 90 | + log "Version $VERSION → $NEW_VERSION" |
| 91 | + sed -i "0,/^version *= *\".*\"/s//version = \"$NEW_VERSION\"/" "$ROOT/Cargo.toml" |
| 92 | + cargo update --workspace --offline --quiet --manifest-path "$ROOT/Cargo.toml" |
| 93 | + git -C "$ROOT" commit -q -m "Version $NEW_VERSION" -- Cargo.toml Cargo.lock |
| 94 | + VERSION="$NEW_VERSION" |
| 95 | + fi |
| 96 | + |
| 97 | + if (( RELEASE )); then |
| 98 | + TAG="$VERSION" |
| 99 | + ! dirty || die "Commit or stash your changes first: a release is built from a commit" |
| 56 | 100 | HEAD="$(git -C "$ROOT" rev-parse HEAD)" |
| 57 | | - REMOTE_TAG="$(git -C "$ROOT" ls-remote origin "refs/tags/$TAG^{}" "refs/tags/$TAG" | awk 'NR==1 || /\^\{\}$/ {sha=$1} END {print sha}')" |
| 58 | | - LOCAL_TAG="$(git -C "$ROOT" rev-parse -q --verify "refs/tags/$TAG^{commit}" || true)" |
| 59 | | - for tagged in "$REMOTE_TAG" "$LOCAL_TAG"; do |
| 60 | | - [[ -z "$tagged" || "$tagged" == "$HEAD" ]] || |
| 61 | | - die "$TAG is already tagged at ${tagged:0:7}; bump the version in Cargo.toml for a new release" |
| 62 | | - done |
| 101 | + check_tag "$TAG" "$HEAD" |
| 63 | 102 | log "Releasing $TAG of $REPO on $SERVER from ${HEAD:0:7}" |
| 64 | 103 | fi |
| 65 | 104 | |
| @@ -146,14 +185,18 @@ if (( RELEASE )); then |
| 146 | 185 | git -C "$ROOT" push -q origin "refs/tags/$TAG" |
| 147 | 186 | fi |
| 148 | 187 | |
| 149 | | - for file in "$STAGE"/*; do |
| 150 | | - name="$(basename "$file")" |
| 188 | + # The checksums go last, so the release never shows them alone. |
| 189 | + for name in "$(basename "$OUT")" "$NAME-$VERSION-$ARCH" SHA256SUMS; do |
| 190 | + file="$STAGE/$name" |
| 151 | 191 | log "Uploading $name" |
| 152 | 192 | # The token goes in through a file descriptor, so it never shows in ps. |
| 153 | 193 | if ! reply="$(curl -sS --fail-with-body -T "$file" -H @<(printf 'Authorization: Bearer %s\n' "$TOKEN") \ |
| 154 | | - "$SERVER/$REPO/releases/download/$TAG/$name")"; then |
| 155 | | - die "Uploading $name failed: $reply" |
| 194 | + "$SERVER/$REPO/releases/download/$TAG/$name" 2>&1)"; then |
| 195 | + die "Uploading $name failed: $reply (run the same command again to retry)" |
| 156 | 196 | fi |
| 157 | 197 | done |
| 158 | 198 | log "Released: $SERVER/$REPO/releases/tag/$TAG" |
| 159 | 199 | fi |
| 200 | + if [[ -n "$NEW_VERSION" ]] && [[ -n "$(git -C "$ROOT" log --oneline '@{upstream}..HEAD' 2>/dev/null || echo unpushed)" ]]; then |
| 201 | + log "The version commit is only on your $(git -C "$ROOT" branch --show-current) branch here: git push to share it" |
| 202 | + fi |