v0.0.40 05d0df9

install.sh RawHistoryFolder

1#!/usr/bin/env bash
2#
3# zot installer.
4#
5# Usage:
6#   curl -fsSL https://raw.githubusercontent.com/patriceckhart/zot/main/install.sh | bash
7#   curl -fsSL https://raw.githubusercontent.com/patriceckhart/zot/main/install.sh | bash -s -- v0.0.1 ~/bin
8#
9# Positional arguments:
10#   $1  version    — release tag (e.g. v0.0.1). Defaults to "latest".
11#   $2  prefix     — install directory. Defaults to the first writable
12#                    directory in: /usr/local/bin, $HOME/.local/bin,
13#                    $HOME/bin. Created if missing. Add it to your PATH
14#                    if it isn't already.
15#
16# Environment overrides:
17#   ZOT_VERSION    same as $1
18#   ZOT_PREFIX     same as $2
19#   GITHUB_TOKEN   personal access token — required while the repo is
20#                  private, ignored once it goes public. Must have at
21#                  least `contents:read` scope on the zot repository.
22#
23# The script detects your OS and architecture, downloads the matching
24# archive from the GitHub release, verifies the sha256 against the
25# release's checksums.txt, extracts the binary, and moves it into the
26# prefix directory. No sudo unless you explicitly pick a prefix that
27# needs it.
28
29set -euo pipefail
30
31OWNER="patriceckhart"
32REPO="zot"
33BINARY="zot"
34
35VERSION="${1:-${ZOT_VERSION:-latest}}"
36PREFIX="${2:-${ZOT_PREFIX:-}}"
37
38msg()  { printf "\033[1m==>\033[0m %s\n" "$*"; }
39warn() { printf "\033[33mwarn:\033[0m %s\n" "$*" >&2; }
40die()  { printf "\033[31merror:\033[0m %s\n" "$*" >&2; exit 1; }
41
42command -v curl >/dev/null 2>&1 || die "curl is required"
43command -v tar  >/dev/null 2>&1 || die "tar is required"
44
45# CURL_AUTH is prepended to every curl invocation so private-repo
46# downloads work while $GITHUB_TOKEN is set. Empty string when the repo
47# becomes public.
48CURL_AUTH=()
49if [ -n "${GITHUB_TOKEN:-}" ]; then
50  CURL_AUTH=(-H "Authorization: Bearer $GITHUB_TOKEN")
51fi
52
53# ---- detect OS + arch ----
54
55uname_s=$(uname -s)
56uname_m=$(uname -m)
57
58case "$uname_s" in
59  Linux)   OS=linux ;;
60  Darwin)  OS=darwin ;;
61  MINGW*|MSYS*|CYGWIN*)
62    die "windows detected — use install.ps1 from powershell instead"
63    ;;
64  *) die "unsupported os: $uname_s" ;;
65esac
66
67case "$uname_m" in
68  x86_64|amd64)         ARCH=amd64 ;;
69  arm64|aarch64)        ARCH=arm64 ;;
70  *) die "unsupported arch: $uname_m" ;;
71esac
72
73# ---- resolve version ----
74
75if [ "$VERSION" = "latest" ]; then
76  # Private-repo friendly: hit the api, grab tag_name. Falls back to
77  # following the /releases/latest redirect on public repos.
78  if [ ${#CURL_AUTH[@]} -gt 0 ]; then
79    VERSION=$(curl -fsSL "${CURL_AUTH[@]}" \
80      "https://api.github.com/repos/${OWNER}/${REPO}/releases/latest" \
81      | sed -nE 's/.*"tag_name": *"([^"]+)".*/\1/p' | head -n1)
82  else
83    VERSION=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
84      "https://github.com/${OWNER}/${REPO}/releases/latest" \
85      | sed -E 's|.*/tag/([^/]+).*|\1|')
86  fi
87  [ -n "$VERSION" ] || die "could not resolve latest version (set GITHUB_TOKEN if the repo is private)"
88fi
89
90case "$VERSION" in v*) ;; *) VERSION="v$VERSION" ;; esac
91VER_NUM="${VERSION#v}"
92
93# ---- pick an install prefix ----
94
95pick_prefix() {
96  local candidates=()
97  [ -n "$PREFIX" ] && { echo "$PREFIX"; return; }
98  candidates+=("/usr/local/bin")
99  [ -n "${HOME:-}" ] && candidates+=("$HOME/.local/bin" "$HOME/bin")
100  for d in "${candidates[@]}"; do
101    if [ -d "$d" ] && [ -w "$d" ]; then
102      echo "$d"
103      return
104    fi
105  done
106  # Nothing writable yet — create ~/.local/bin and use that.
107  if [ -n "${HOME:-}" ]; then
108    mkdir -p "$HOME/.local/bin"
109    echo "$HOME/.local/bin"
110    return
111  fi
112  die "no writable install prefix found; pass one as the second argument"
113}
114
115PREFIX=$(pick_prefix)
116mkdir -p "$PREFIX"
117
118# ---- download + verify + extract ----
119
120ARCHIVE="${BINARY}_${VER_NUM}_${OS}_${ARCH}.tar.gz"
121BASE_URL="https://github.com/${OWNER}/${REPO}/releases/download/${VERSION}"
122ARCHIVE_URL="${BASE_URL}/${ARCHIVE}"
123CHECKSUMS_URL="${BASE_URL}/checksums.txt"
124
125TMP=$(mktemp -d)
126trap 'rm -rf "$TMP"' EXIT
127
128msg "downloading ${ARCHIVE}"
129curl -fsSL "${CURL_AUTH[@]}" -o "$TMP/$ARCHIVE" "$ARCHIVE_URL" \
130  || die "download failed: $ARCHIVE_URL (set GITHUB_TOKEN if the repo is private)"
131
132msg "verifying checksum"
133curl -fsSL "${CURL_AUTH[@]}" -o "$TMP/checksums.txt" "$CHECKSUMS_URL" \
134  || die "download failed: $CHECKSUMS_URL"
135
136expected=$(grep " ${ARCHIVE}\$" "$TMP/checksums.txt" | awk '{print $1}' || true)
137[ -n "$expected" ] || die "no checksum for $ARCHIVE in checksums.txt"
138
139if command -v sha256sum >/dev/null 2>&1; then
140  actual=$(sha256sum "$TMP/$ARCHIVE" | awk '{print $1}')
141elif command -v shasum >/dev/null 2>&1; then
142  actual=$(shasum -a 256 "$TMP/$ARCHIVE" | awk '{print $1}')
143else
144  die "no sha256 tool found (sha256sum or shasum)"
145fi
146
147[ "$expected" = "$actual" ] \
148  || die "checksum mismatch: expected $expected, got $actual"
149
150msg "extracting"
151tar -xzf "$TMP/$ARCHIVE" -C "$TMP"
152
153[ -f "$TMP/$BINARY" ] || die "archive did not contain a '$BINARY' binary"
154
155msg "installing to $PREFIX/$BINARY"
156install -m 0755 "$TMP/$BINARY" "$PREFIX/$BINARY" 2>/dev/null \
157  || { cp "$TMP/$BINARY" "$PREFIX/$BINARY" && chmod 0755 "$PREFIX/$BINARY"; }
158
159# ---- PATH hint ----
160
161case ":$PATH:" in
162  *":$PREFIX:"*) ;;
163  *)
164    warn "$PREFIX is not on your PATH"
165    warn "add this to your shell rc file:"
166    warn "  export PATH=\"$PREFIX:\$PATH\""
167    ;;
168esac
169
170msg "installed $("$PREFIX/$BINARY" --version 2>/dev/null || echo zot)"
171msg "run:  zot          (interactive tui)"
172msg "run:  zot --help   (all flags and subcommands)"

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 1

Tags 476

↑↓ moveenter openesc close