-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·187 lines (162 loc) · 5.25 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·187 lines (162 loc) · 5.25 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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/usr/bin/env bash
# install.sh — install gh-toolkit by symlinking bin/gh-toolkit into a PATH directory
#
# Usage:
# ./install.sh install (default target: /usr/local/bin)
# ./install.sh --target-dir <path> override target dir (used by tests)
# ./install.sh --uninstall remove the symlink
# ./install.sh --dry-run preview without changes
# ./install.sh --help show help
#
# Behavior:
# - Idempotent. Re-running on a correctly-installed system reports EXISTS.
# - If the symlink exists but points elsewhere, refuses to replace unless --force.
# - Uses sudo only when the target directory is not writable by the caller.
# - Verifies the target directory is in PATH; warns if not.
#
# Output (machine-parseable):
# CREATED:link/<target> symlink created
# EXISTS:link/<target> already pointing to the right binary
# REPLACED:link/<target> stale symlink replaced (with --force)
# DELETED:link/<target> symlink removed (--uninstall)
# SKIPPED:link/<target>:<reason> not present (uninstall) / target unchanged
# DRY_RUN:link/<target>:<action> preview line
# FAILED:link/<target>:<msg> error
#
# Exit codes:
# 0 success (or already in desired state)
# 1 user input error / permission error / stale link without --force
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SOURCE_BIN="$SCRIPT_DIR/bin/gh-toolkit"
# --- Defaults ---
TARGET_DIR="/usr/local/bin"
LINK_NAME="gh-toolkit"
DRY_RUN=false
UNINSTALL=false
FORCE=false
usage() {
cat <<EOF
Usage: ./install.sh [OPTIONS]
Install gh-toolkit by creating a symlink in a directory on your PATH.
Options:
--target-dir <path> target directory (default: /usr/local/bin)
--uninstall remove the symlink
--force replace symlink if it points elsewhere
--dry-run preview without making changes
--help, -h show this message
Examples:
./install.sh # install to /usr/local/bin (sudo if needed)
./install.sh --target-dir ~/.local/bin # user-local install (no sudo)
./install.sh --uninstall # remove from /usr/local/bin
./install.sh --dry-run # preview the install action
EOF
}
# --- Parse args ---
while [[ $# -gt 0 ]]; do
case "$1" in
--target-dir)
TARGET_DIR="${2:-}"
[[ -z "$TARGET_DIR" ]] && { echo "FAILED:link/${LINK_NAME}:--target-dir requires a path" >&2; exit 1; }
shift 2
;;
--uninstall) UNINSTALL=true; shift ;;
--force) FORCE=true; shift ;;
--dry-run) DRY_RUN=true; shift ;;
--help|-h) usage; exit 0 ;;
*)
echo "FAILED:link/${LINK_NAME}:unknown argument: $1" >&2
usage >&2
exit 1
;;
esac
done
TARGET_LINK="$TARGET_DIR/$LINK_NAME"
DISPLAY="$TARGET_LINK"
# --- Validation ---
if [[ ! -f "$SOURCE_BIN" ]]; then
echo "FAILED:link/${DISPLAY}:source not found: $SOURCE_BIN" >&2
exit 1
fi
if [[ ! -x "$SOURCE_BIN" ]]; then
echo "FAILED:link/${DISPLAY}:source not executable: $SOURCE_BIN" >&2
exit 1
fi
if [[ ! -d "$TARGET_DIR" ]]; then
echo "FAILED:link/${DISPLAY}:target dir does not exist: $TARGET_DIR" >&2
exit 1
fi
# --- Determine if sudo is needed for target dir ---
SUDO=""
if [[ ! -w "$TARGET_DIR" ]]; then
if command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
else
echo "FAILED:link/${DISPLAY}:no write access to $TARGET_DIR and sudo unavailable" >&2
exit 1
fi
fi
# --- Helper: read symlink target portably (macOS readlink lacks -f) ---
resolve_link() {
local link="$1"
if [[ -L "$link" ]]; then
readlink "$link"
fi
}
# --- Uninstall path ---
if [[ "$UNINSTALL" == true ]]; then
if [[ ! -e "$TARGET_LINK" && ! -L "$TARGET_LINK" ]]; then
echo "SKIPPED:link/${DISPLAY}:not present"
exit 0
fi
if [[ ! -L "$TARGET_LINK" ]]; then
echo "FAILED:link/${DISPLAY}:not a symlink (refusing to delete real file)" >&2
exit 1
fi
if [[ "$DRY_RUN" == true ]]; then
echo "DRY_RUN:link/${DISPLAY}:would remove symlink"
exit 0
fi
$SUDO rm -f "$TARGET_LINK"
echo "DELETED:link/${DISPLAY}"
exit 0
fi
# --- Install path ---
if [[ -L "$TARGET_LINK" ]]; then
current=$(resolve_link "$TARGET_LINK")
if [[ "$current" == "$SOURCE_BIN" ]]; then
echo "EXISTS:link/${DISPLAY}"
exit 0
fi
if [[ "$FORCE" != true ]]; then
echo "FAILED:link/${DISPLAY}:already points to ${current} (use --force to replace)" >&2
exit 1
fi
if [[ "$DRY_RUN" == true ]]; then
echo "DRY_RUN:link/${DISPLAY}:would replace stale link (was: ${current})"
exit 0
fi
$SUDO rm -f "$TARGET_LINK"
$SUDO ln -s "$SOURCE_BIN" "$TARGET_LINK"
echo "REPLACED:link/${DISPLAY}"
elif [[ -e "$TARGET_LINK" ]]; then
echo "FAILED:link/${DISPLAY}:not a symlink (refusing to overwrite real file)" >&2
exit 1
else
if [[ "$DRY_RUN" == true ]]; then
echo "DRY_RUN:link/${DISPLAY}:would create symlink → $SOURCE_BIN"
exit 0
fi
$SUDO ln -s "$SOURCE_BIN" "$TARGET_LINK"
echo "CREATED:link/${DISPLAY}"
fi
# --- PATH check (informational, doesn't fail) ---
case ":$PATH:" in
*":$TARGET_DIR:"*)
: # in PATH
;;
*)
echo "WARN:path/${TARGET_DIR}:not in current PATH — open a new shell or add it manually" >&2
;;
esac
exit 0