detached ece7682

install.sh RawHistoryFolder

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

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