Repository navigation
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·91 lines (84 loc) · 4.07 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·91 lines (84 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env bash
# safeclaude installer — install the host wrapper and point it at the published
# image (no clone needed; clone only to build/hack). The wrapper is fetched over
# HTTPS from GitHub and sourced into your shell rc — there is no checksum, so
# trust the source. https://github.com/kurtb/safeclaude
#
# curl -fsSL https://github.com/kurtb/safeclaude/releases/latest/download/install.sh | bash
#
# Env: SAFECLAUDE_VERSION pin wrapper + image to a release (e.g. 0.2.0), or a
# branch name for the wrapper. Default: the newest
# release, re-resolved on each run (wrapper + image move
# together, both pinned to that release version).
# SAFECLAUDE_IMAGE a CUSTOM (non-ghcr) image ref. A value pointing at
# ghcr.io/kurtb/safeclaude is ignored as a stale
# prior-install value — pin with SAFECLAUDE_VERSION.
# SAFECLAUDE_HOME / SAFECLAUDE_RC install dir / shell rc
set -euo pipefail
REPO="kurtb/safeclaude"
DEST="${SAFECLAUDE_HOME:-$HOME/.local/share/safeclaude}"
RC="${SAFECLAUDE_RC:-$HOME/.zshrc}"
WRAPPER="$DEST/safeclaude.zsh"
say() { printf 'safeclaude-install: %s\n' "$*" >&2; }
command -v curl >/dev/null || { say "curl is required"; exit 1; }
# Resolve the wrapper ref and image tag — they move together, both tracking a
# RELEASE (not the moving :latest, which follows main).
# SAFECLAUDE_VERSION set → pin both to that release.
# unset → both track the newest release, RE-RESOLVED on every
# install/upgrade (so upgrade bumps wrapper + image
# together). A branch name pins the wrapper to that
# branch with image :latest.
sel="${SAFECLAUDE_VERSION:-}"
pinned=0
if [ -n "$sel" ]; then
pinned=1
else
sel="$(curl -fsSL -o /dev/null -w '%{url_effective}' \
"https://github.com/$REPO/releases/latest" 2>/dev/null \
| sed -n 's#.*/releases/tag/##p' || true)"
[ -n "$sel" ] || sel="main"
fi
if [[ "$sel" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
ref="v${sel#v}"; tag="${sel#v}" # pin wrapper + image to this release
elif [ "$pinned" = 1 ] && [[ "$sel" =~ ^v?[0-9]+(\.[0-9]+)?$ ]]; then
say "SAFECLAUDE_VERSION must be a full X.Y.Z (e.g. 0.3.0)"
exit 1
else
ref="$sel"; tag="latest" # a branch (e.g. main)
fi
# Image: a genuine custom SAFECLAUDE_IMAGE (any ref that isn't our own ghcr
# repo) always wins. A value that points at ghcr.io/$REPO — in ANY form (`:tag`,
# `@digest`, or bare) — is IGNORED as a stale prior-install value; that leak was
# the freeze bug. The image is re-resolved each run, so `safeclaude upgrade`
# moves it forward. Pin a version with SAFECLAUDE_VERSION.
IMAGE="ghcr.io/$REPO:$tag"
if [ -n "${SAFECLAUDE_IMAGE:-}" ]; then
case "$SAFECLAUDE_IMAGE" in
ghcr.io/"$REPO" | ghcr.io/"$REPO":* | ghcr.io/"$REPO"@*) : ;; # ours → ignore
*) IMAGE="$SAFECLAUDE_IMAGE" ;; # genuine custom
esac
fi
say "installing wrapper @ $ref, image $IMAGE"
# Download to a temp file; replace the wrapper only on success (a failed/404
# download never clobbers a working install).
mkdir -p "$DEST"
tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT
curl -fsSL "https://raw.githubusercontent.com/$REPO/$ref/safeclaude.zsh" -o "$tmp"
mv "$tmp" "$WRAPPER"
# Update the rc in place (preserves a symlinked rc and its perms). Replace an
# existing block only when BOTH markers are present, else append.
S="# >>> safeclaude >>>"; E="# <<< safeclaude <<<"
block="$S
export SAFECLAUDE_IMAGE=\"$IMAGE\"
source \"$WRAPPER\"
$E"
touch "$RC"
if grep -qF "$S" "$RC" && grep -qF "$E" "$RC"; then
kept="$(awk -v s="$S" -v e="$E" '$0==s{skip=1} !skip{print} $0==e{skip=0}' "$RC")"
printf '%s\n%s\n' "$kept" "$block" > "$RC"
else
printf '\n%s\n' "$block" >> "$RC"
fi
say "done → $WRAPPER"
say "open a new shell (or: source $RC), then run 'safeclaude' in a project"
command -v docker >/dev/null || say "note: docker not found — install it to use safeclaude"