v0.5.0 c37833b

install.sh RawHistoryFolder

1#!/bin/sh
2# vev installer — Linux x86_64/arm64 and macOS arm64.
3# Usage: curl -fsSL https://raw.githubusercontent.com/bnema/vev/main/install.sh | sh
4# Env: VEV_VERSION=vX.Y.Z to pin a version (default: latest release).
5set -eu
6
7REPO="bnema/vev"
8VERSION="${VEV_VERSION:-latest}"
9
10info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
11err()  { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
12
13# --- Detect platform -------------------------------------------------------
14OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
15ARCH="$(uname -m)"
16case "$OS/$ARCH" in
17  linux/x86_64|linux/amd64)  TARBALL="vev_linux_x86_64.tar.gz" ;;
18  linux/aarch64|linux/arm64) TARBALL="vev_linux_arm64.tar.gz" ;;
19  darwin/arm64)              TARBALL="vev_darwin_arm64.tar.gz" ;;
20  *) err "unsupported platform: $OS/$ARCH (supported: linux/x86_64, linux/arm64, darwin/arm64)" ;;
21esac
22
23# --- Pick install dir ------------------------------------------------------
24if [ -d "$HOME/.local/bin" ]; then
25  INSTALL_DIR="$HOME/.local/bin"
26else
27  INSTALL_DIR="/usr/local/bin"
28fi
29
30# --- Resolve version -------------------------------------------------------
31if [ "$VERSION" = "latest" ]; then
32  VERSION="$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \
33    | grep '"tag_name"' | head -1 | cut -d '"' -f 4)"
34  [ -n "$VERSION" ] || err "could not resolve the latest release tag"
35fi
36info "installing vev ${VERSION} (${OS}/${ARCH}) to ${INSTALL_DIR}"
37
38# --- Download + verify -----------------------------------------------------
39TMP_DIR="$(mktemp -d)"
40trap 'rm -rf "$TMP_DIR"' EXIT
41
42BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}"
43curl -fsSL -o "$TMP_DIR/$TARBALL" "$BASE_URL/$TARBALL" \
44  || err "download failed: $BASE_URL/$TARBALL"
45curl -fsSL -o "$TMP_DIR/checksums.txt" "$BASE_URL/checksums.txt" \
46  || err "download failed: $BASE_URL/checksums.txt"
47
48cd "$TMP_DIR"
49EXPECTED="$(grep "  $TARBALL\$" checksums.txt | cut -d ' ' -f 1)"
50[ -n "$EXPECTED" ] || err "$TARBALL not found in checksums.txt"
51if command -v sha256sum >/dev/null 2>&1; then
52  ACTUAL="$(sha256sum "$TARBALL" | cut -d ' ' -f 1)"
53else
54  ACTUAL="$(shasum -a 256 "$TARBALL" | cut -d ' ' -f 1)"
55fi
56[ "$EXPECTED" = "$ACTUAL" ] || err "checksum mismatch for $TARBALL (expected $EXPECTED, got $ACTUAL)"
57info "checksum verified"
58
59# --- Extract + install -----------------------------------------------------
60tar -xzf "$TARBALL"
61
62# Keep this list in the archive's required installation order.  All payloads
63# are checked and staged before a live file is replaced.
64PAYLOADS="vev scripts/vev-bar-top-right scripts/vev-bar-bottom-right"
65TRANSACTION=".vev-install-$$"
66
67path_exists() {
68  [ -e "$1" ] || [ -L "$1" ]
69}
70
71install_path() {
72  # Run every destination mutation through the same privilege path.
73  if [ -w "$INSTALL_DIR" ]; then
74    "$@"
75  else
76    sudo "$@"
77  fi
78}
79
80ensure_install_dir() {
81  [ -d "$INSTALL_DIR" ] && return 0
82
83  if [ -w "$(dirname "$INSTALL_DIR")" ]; then
84    mkdir -p "$INSTALL_DIR" || err "cannot create $INSTALL_DIR"
85  else
86    info "escalating with sudo to create $INSTALL_DIR"
87    sudo mkdir -p "$INSTALL_DIR" || err "cannot create $INSTALL_DIR"
88  fi
89}
90
91stage_path() {
92  printf '%s/%s-%s.new\n' "$INSTALL_DIR" "$TRANSACTION" "${1##*/}"
93}
94
95backup_path() {
96  printf '%s/%s-%s.bak\n' "$INSTALL_DIR" "$TRANSACTION" "${1##*/}"
97}
98
99remove_destination_file() {
100  install_path rm -f "$1"
101}
102
103cleanup_staging() {
104  for payload in $PAYLOADS; do
105    stage="$(stage_path "$payload")"
106    remove_destination_file "$stage" || return 1
107  done
108}
109
110rollback_install() {
111  rollback_ok=true
112
113  for payload in $PAYLOADS; do
114    name="${payload##*/}"
115    target="$INSTALL_DIR/$name"
116    stage="$(stage_path "$payload")"
117    backup="$(backup_path "$payload")"
118
119    if path_exists "$backup"; then
120      # A prior file was moved aside: remove the new one and put it back.
121      remove_destination_file "$target" || rollback_ok=false
122      install_path mv "$backup" "$target" || rollback_ok=false
123    elif ! path_exists "$stage"; then
124      # No backup means the original was absent. A missing stage was moved
125      # into place, so restore that original absence.
126      remove_destination_file "$target" || rollback_ok=false
127    fi
128    remove_destination_file "$stage" || rollback_ok=false
129  done
130
131  [ "$rollback_ok" = true ]
132}
133
134preflight_payloads() {
135  for payload in $PAYLOADS; do
136    [ -f "$payload" ] && [ -r "$payload" ] \
137      || err "archive is missing readable payload: $payload"
138  done
139}
140
141# Do not create or alter the destination until every archive payload exists.
142preflight_payloads
143ensure_install_dir
144
145# Stage every payload in the destination first, so each later mv is a
146# same-filesystem replacement. A staging failure leaves live files untouched.
147for source in $PAYLOADS; do
148  stage="$(stage_path "$source")"
149  if ! install_path install -m 755 "$source" "$stage"; then
150    cleanup_staging || err "failed to stage payloads and clean staging files"
151    err "failed to stage payload: $source"
152  fi
153done
154
155# Replace each live file only after all three staged copies are ready. Existing
156# files are retained as backups until the full transaction succeeds.
157for source in $PAYLOADS; do
158  name="${source##*/}"
159  target="$INSTALL_DIR/$name"
160  stage="$(stage_path "$source")"
161  backup="$(backup_path "$source")"
162
163  if path_exists "$target" && ! install_path mv "$target" "$backup"; then
164    rollback_install || err "failed to preserve $target and roll back installation"
165    err "failed to preserve existing file: $target"
166  fi
167  if ! install_path mv "$stage" "$target"; then
168    rollback_install || err "failed to replace $target and roll back installation"
169    err "failed to install payload: $source"
170  fi
171done
172
173for source in $PAYLOADS; do
174  backup="$(backup_path "$source")"
175  remove_destination_file "$backup" \
176    || err "installed payloads but failed to clean backup: $backup"
177done
178
179info "installed: $INSTALL_DIR/vev"
180case ":${PATH:-}:" in
181  *":$INSTALL_DIR:"*) "$INSTALL_DIR/vev" --version ;;
182  *) info "note: $INSTALL_DIR is not in your PATH" ;;
183esac

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close