freecode-sh 0.1.0-alpha.42 → 0.1.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/install.sh DELETED
@@ -1,687 +0,0 @@
1
- #!/bin/sh
2
-
3
- # Free Code native installer for macOS.
4
- # Review this script before running it: https://freecode.sh/install
5
-
6
- set -eu
7
-
8
- DEFAULT_CHANNEL="alpha"
9
- DEFAULT_DOWNLOAD_BASE_URL="https://releases.freecode.sh/releases"
10
- DEFAULT_CHANNEL_BASE_URL="https://releases.freecode.sh/channels"
11
- EXPECTED_TEAM_IDENTIFIER="GD597P8KJ2"
12
- # Apple code requirement language. "anchor apple generic" forces the signing
13
- # chain to terminate at Apple's root certificate, so only a real Developer ID
14
- # certificate issued to this team can satisfy it. Text printed by
15
- # `codesign --display` cannot be trusted for this: Authority and TeamIdentifier
16
- # lines are read straight out of the certificate, so a self-signed certificate
17
- # can present any subject and OU it likes.
18
- RELEASE_CODE_REQUIREMENT="=anchor apple generic and certificate leaf[subject.OU] = \"$EXPECTED_TEAM_IDENTIFIER\""
19
- CODESIGN_BIN="/usr/bin/codesign"
20
- LEGACY_ALPHA6_MANIFEST_SHA256="1bf63e0d6114b12d61c07094936bd2606ae82ce87d5b054fbfb01c5f3d3646d8"
21
- LAUNCHER_RELATIVE_PATH="bin/freecode"
22
- INTERFACE_RELATIVE_PATH="libexec/freecode/freecode-tui"
23
- ENGINE_RELATIVE_PATH="libexec/freecode/freecode-engine"
24
-
25
- VERSION="${FREECODE_INSTALL_VERSION:-}"
26
- CHANNEL="${FREECODE_INSTALL_CHANNEL:-$DEFAULT_CHANNEL}"
27
- DOWNLOAD_BASE_URL="${FREECODE_INSTALL_BASE_URL:-$DEFAULT_DOWNLOAD_BASE_URL}"
28
- CHANNEL_BASE_URL="${FREECODE_CHANNEL_BASE_URL:-$DEFAULT_CHANNEL_BASE_URL}"
29
- INSTALL_ROOT="${FREECODE_INSTALL_ROOT:-${HOME:?HOME is required}/.local/share/freecode}"
30
- BIN_DIR="${FREECODE_BIN_DIR:-${HOME}/.local/bin}"
31
- OS_NAME="${FREECODE_INSTALL_OS:-$(uname -s)}"
32
- MACHINE="${FREECODE_INSTALL_ARCH:-$(uname -m)}"
33
- TEMP_DIR=""
34
- STAGE_DIR=""
35
- INSTALL_LOCK_PATH=""
36
- INSTALL_LOCK_HELD="false"
37
- INSTALL_LOCK_OWNER_TOKEN=""
38
- INSTALL_LOCK_GUARD_HELD="false"
39
- ALLOW_UNSIGNED_INSTALL="${FREECODE_ALLOW_UNSIGNED_INSTALL:-false}"
40
- EMBEDDED_INSTALL="${FREECODE_INSTALL_EMBEDDED:-false}"
41
- LAUNCH_AFTER_INSTALL="${FREECODE_INSTALL_LAUNCH:-true}"
42
- RELEASE_MODE=""
43
- PATH_PROFILE=""
44
- PATH_CONFIGURED="false"
45
-
46
- usage() {
47
- cat <<'EOF'
48
- Install Free Code's native macOS release.
49
-
50
- Usage:
51
- curl -fsSL https://freecode.sh/install.sh | sh
52
- curl -fsSL https://freecode.sh/install.sh | sh -s -- --version 0.1.0-alpha.1
53
-
54
- Options:
55
- --version VERSION Install an exact immutable version.
56
- --channel CHANNEL Resolve a channel (default: alpha).
57
- --base-url URL Override the immutable release download base URL.
58
- --channel-base-url URL Override the channel manifest base URL.
59
- --install-root PATH Version store (default: ~/.local/share/freecode).
60
- --bin-dir PATH Executable link directory (default: ~/.local/bin).
61
- --no-launch Install without opening Free Code.
62
- -h, --help Show this help.
63
-
64
- Environment equivalents:
65
- FREECODE_INSTALL_VERSION, FREECODE_INSTALL_CHANNEL,
66
- FREECODE_INSTALL_BASE_URL, FREECODE_CHANNEL_BASE_URL,
67
- FREECODE_INSTALL_ROOT, FREECODE_BIN_DIR, FREECODE_INSTALL_LAUNCH
68
-
69
- Internal testing only:
70
- FREECODE_ALLOW_UNSIGNED_INSTALL=true permits an explicitly marked unsigned
71
- release outside the alpha channel. Never use this for a normal install.
72
- EOF
73
- }
74
-
75
- fail() {
76
- printf 'freecode installer: %s\n' "$*" >&2
77
- exit 1
78
- }
79
-
80
- cleanup() {
81
- if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
82
- rm -rf "$TEMP_DIR"
83
- fi
84
- if [ -n "$STAGE_DIR" ] && [ -d "$STAGE_DIR" ]; then
85
- rm -rf "$STAGE_DIR"
86
- fi
87
- release_install_lock
88
- }
89
-
90
- require_value() {
91
- [ "$#" -ge 2 ] || fail "$1 requires a value"
92
- [ -n "$2" ] || fail "$1 requires a non-empty value"
93
- }
94
-
95
- release_install_lock() {
96
- if [ "$INSTALL_LOCK_HELD" = "true" ]; then
97
- lock_owner=$(sed -n '1p' "$INSTALL_LOCK_PATH" 2>/dev/null || true)
98
- lock_owner_token=$(sed -n '2p' "$INSTALL_LOCK_PATH" 2>/dev/null || true)
99
- if [ "$lock_owner" = "$$" ] && [ "$lock_owner_token" = "$INSTALL_LOCK_OWNER_TOKEN" ]; then
100
- rm -f "$INSTALL_LOCK_PATH"
101
- fi
102
- fi
103
- INSTALL_LOCK_HELD="false"
104
- if [ "$INSTALL_LOCK_GUARD_HELD" = "true" ]; then
105
- exec 9>&-
106
- INSTALL_LOCK_GUARD_HELD="false"
107
- fi
108
- }
109
-
110
- create_install_lock() {
111
- (
112
- umask 077
113
- set -C
114
- printf '%s\n%s\n' "$$" "$INSTALL_LOCK_OWNER_TOKEN" > "$INSTALL_LOCK_PATH"
115
- ) 2>/dev/null
116
- }
117
-
118
- process_start_token() {
119
- LC_ALL=C ps -o lstart= -p "$1" 2>/dev/null \
120
- | sed -n '1{s/^[[:space:]]*//;s/[[:space:]]*$//;p;}'
121
- }
122
-
123
- acquire_install_lock() {
124
- INSTALL_LOCK_PATH="$INSTALL_ROOT/.update-lock"
125
- INSTALL_LOCK_OWNER_TOKEN=$(process_start_token "$$")
126
- [ -n "$INSTALL_LOCK_OWNER_TOKEN" ] \
127
- || fail "could not identify the installer process for update locking"
128
- mkdir -p "$INSTALL_ROOT"
129
- exec 9>"$INSTALL_LOCK_PATH.guard"
130
- # Descriptor-mode locks remain attached to fd 9 after the helper exits and
131
- # are held until release_install_lock closes the descriptor.
132
- if [ -x /usr/bin/lockf ]; then
133
- guard_acquired=false
134
- /usr/bin/lockf -s -t 120 9 && guard_acquired=true
135
- elif [ "$(uname -s)" = "Linux" ] && command -v flock >/dev/null 2>&1; then
136
- # The Linux CI host emulates Darwin to exercise this macOS-only installer.
137
- guard_acquired=false
138
- flock -w 120 9 && guard_acquired=true
139
- else
140
- exec 9>&-
141
- fail "the system lock utility is unavailable"
142
- fi
143
- if [ "$guard_acquired" != "true" ]; then
144
- exec 9>&-
145
- fail "another Free Code install or update is still in progress"
146
- fi
147
- INSTALL_LOCK_GUARD_HELD="true"
148
- attempts=0
149
- while ! create_install_lock; do
150
- # Read both the regular-file protocol and the short-lived directory format
151
- # used by development builds before the lock contract was unified.
152
- if [ -d "$INSTALL_LOCK_PATH" ]; then
153
- lock_owner=$(sed -n '1p' "$INSTALL_LOCK_PATH/pid" 2>/dev/null || true)
154
- lock_owner_token=""
155
- else
156
- lock_owner=$(sed -n '1p' "$INSTALL_LOCK_PATH" 2>/dev/null || true)
157
- lock_owner_token=$(sed -n '2p' "$INSTALL_LOCK_PATH" 2>/dev/null || true)
158
- fi
159
- case "$lock_owner" in
160
- ''|*[!0-9]*) ;;
161
- *)
162
- if ! kill -0 "$lock_owner" 2>/dev/null; then
163
- rm -rf "$INSTALL_LOCK_PATH"
164
- continue
165
- fi
166
- if [ -n "$lock_owner_token" ]; then
167
- current_owner_token=$(process_start_token "$lock_owner")
168
- if [ -n "$current_owner_token" ] && [ "$current_owner_token" != "$lock_owner_token" ]; then
169
- rm -rf "$INSTALL_LOCK_PATH"
170
- continue
171
- fi
172
- fi
173
- ;;
174
- esac
175
- attempts=$((attempts + 1))
176
- [ "$attempts" -lt 120 ] \
177
- || fail "another Free Code install or update is still in progress"
178
- sleep 1
179
- done
180
- INSTALL_LOCK_HELD="true"
181
-
182
- if [ -n "${FREECODE_TEST_HOLD_INSTALL_LOCK_SECONDS:-}" ]; then
183
- case "$DOWNLOAD_BASE_URL" in
184
- file://*) sleep "$FREECODE_TEST_HOLD_INSTALL_LOCK_SECONDS" ;;
185
- *) fail "the test install delay is permitted only with local file URLs" ;;
186
- esac
187
- fi
188
- }
189
-
190
- configure_profile() {
191
- profile=$1
192
- path_line=$2
193
- legacy_path_line=${3:-}
194
- profile_dir=$(dirname "$profile")
195
-
196
- [ -d "$profile_dir" ] || return 1
197
- if [ -e "$profile" ] || [ -L "$profile" ]; then
198
- [ -f "$profile" ] || return 1
199
- fi
200
- if grep -Fqx "$path_line" "$profile" 2>/dev/null \
201
- || { [ -n "$legacy_path_line" ] && grep -Fqx "$legacy_path_line" "$profile" 2>/dev/null; }
202
- then
203
- return 0
204
- fi
205
- if [ -s "$profile" ] && ! printf '\n' >> "$profile"; then
206
- return 1
207
- fi
208
- printf '# Free Code\n%s\n' "$path_line" >> "$profile"
209
- }
210
-
211
- configure_path() {
212
- [ "$BIN_DIR" = "$HOME/.local/bin" ] || return 0
213
-
214
- guarded_path_line='case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac'
215
- legacy_path_line='export PATH="$HOME/.local/bin:$PATH"'
216
- detected_shell=${SHELL:-/bin/zsh}
217
-
218
- case "$detected_shell" in
219
- */zsh)
220
- zsh_config_dir=${ZDOTDIR:-$HOME}
221
- if configure_profile "$zsh_config_dir/.zprofile" "$guarded_path_line" "$legacy_path_line" \
222
- && configure_profile "$zsh_config_dir/.zshrc" "$guarded_path_line" "$legacy_path_line"
223
- then
224
- PATH_PROFILE="$zsh_config_dir/.zprofile and $zsh_config_dir/.zshrc"
225
- PATH_CONFIGURED="true"
226
- fi
227
- ;;
228
- */bash)
229
- if [ -e "$HOME/.bash_profile" ] || [ -L "$HOME/.bash_profile" ]; then
230
- bash_login_profile="$HOME/.bash_profile"
231
- elif [ -e "$HOME/.bash_login" ] || [ -L "$HOME/.bash_login" ]; then
232
- bash_login_profile="$HOME/.bash_login"
233
- elif [ -e "$HOME/.profile" ] || [ -L "$HOME/.profile" ]; then
234
- bash_login_profile="$HOME/.profile"
235
- else
236
- bash_login_profile="$HOME/.bash_profile"
237
- fi
238
- if configure_profile "$bash_login_profile" "$guarded_path_line" "$legacy_path_line" \
239
- && configure_profile "$HOME/.bashrc" "$guarded_path_line" "$legacy_path_line"
240
- then
241
- PATH_PROFILE="$bash_login_profile and $HOME/.bashrc"
242
- PATH_CONFIGURED="true"
243
- fi
244
- ;;
245
- */fish)
246
- fish_config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d"
247
- if mkdir -p "$fish_config_dir" 2>/dev/null \
248
- && configure_profile "$fish_config_dir/freecode.fish" \
249
- 'contains -- "$HOME/.local/bin" $PATH; or set -gx PATH "$HOME/.local/bin" $PATH'
250
- then
251
- PATH_PROFILE="$fish_config_dir/freecode.fish"
252
- PATH_CONFIGURED="true"
253
- fi
254
- ;;
255
- esac
256
- }
257
-
258
- trap cleanup EXIT HUP INT TERM
259
-
260
- while [ "$#" -gt 0 ]; do
261
- case "$1" in
262
- --version)
263
- require_value "$@"
264
- VERSION=$2
265
- shift 2
266
- ;;
267
- --channel)
268
- require_value "$@"
269
- CHANNEL=$2
270
- shift 2
271
- ;;
272
- --base-url)
273
- require_value "$@"
274
- DOWNLOAD_BASE_URL=$2
275
- shift 2
276
- ;;
277
- --channel-base-url)
278
- require_value "$@"
279
- CHANNEL_BASE_URL=$2
280
- shift 2
281
- ;;
282
- --install-root)
283
- require_value "$@"
284
- INSTALL_ROOT=$2
285
- shift 2
286
- ;;
287
- --bin-dir)
288
- require_value "$@"
289
- BIN_DIR=$2
290
- shift 2
291
- ;;
292
- --no-launch)
293
- LAUNCH_AFTER_INSTALL="false"
294
- shift
295
- ;;
296
- -h|--help)
297
- usage
298
- exit 0
299
- ;;
300
- --)
301
- shift
302
- break
303
- ;;
304
- *)
305
- fail "unknown option: $1"
306
- ;;
307
- esac
308
- done
309
-
310
- [ "$#" -eq 0 ] || fail "unexpected positional arguments: $*"
311
- command -v curl >/dev/null 2>&1 || fail "curl is required"
312
- command -v tar >/dev/null 2>&1 || fail "tar is required"
313
- command -v shasum >/dev/null 2>&1 || fail "shasum is required"
314
-
315
- case "$ALLOW_UNSIGNED_INSTALL" in
316
- true|false) ;;
317
- *) fail "FREECODE_ALLOW_UNSIGNED_INSTALL must be exactly true or false" ;;
318
- esac
319
-
320
- case "$EMBEDDED_INSTALL" in
321
- true|false) ;;
322
- *) fail "FREECODE_INSTALL_EMBEDDED must be exactly true or false" ;;
323
- esac
324
-
325
- case "$LAUNCH_AFTER_INSTALL" in
326
- true|false) ;;
327
- *) fail "FREECODE_INSTALL_LAUNCH must be exactly true or false" ;;
328
- esac
329
-
330
- case "$OS_NAME" in
331
- Darwin) ;;
332
- *) fail "only macOS is supported right now (detected $OS_NAME)" ;;
333
- esac
334
-
335
- case "$MACHINE" in
336
- arm64|aarch64)
337
- ARCH="arm64"
338
- ;;
339
- x86_64|amd64)
340
- # Prefer the native Apple Silicon build when this shell is running under Rosetta.
341
- if [ "${FREECODE_INSTALL_ARCH:-}" = "" ] && [ "$(sysctl -in sysctl.proc_translated 2>/dev/null || true)" = "1" ]; then
342
- ARCH="arm64"
343
- else
344
- ARCH="x64"
345
- fi
346
- ;;
347
- *)
348
- fail "unsupported Mac architecture: $MACHINE"
349
- ;;
350
- esac
351
-
352
- validate_version() {
353
- printf '%s\n' "$1" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z][0-9A-Za-z.-]*)?$' \
354
- || fail "invalid release version: $1"
355
- }
356
-
357
- validate_channel() {
358
- printf '%s\n' "$1" | grep -Eq '^[0-9A-Za-z][0-9A-Za-z._-]*$' \
359
- || fail "invalid release channel: $1"
360
- }
361
-
362
- validate_release_security() {
363
- manifest_path=$1
364
- context=$2
365
- release_mode=$(sed -n 's/.*"releaseMode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$manifest_path" | head -n 1)
366
- manifest_channel=$(sed -n 's/.*"channel"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$manifest_path" | head -n 1)
367
- case "$release_mode" in
368
- signed-notarized)
369
- RELEASE_MODE="signed-notarized"
370
- ;;
371
- unsigned-override)
372
- if [ "$CHANNEL" = "alpha" ] && [ "$manifest_channel" = "alpha" ]; then
373
- printf 'WARNING: %s is an unsigned Free Code alpha and is not Apple notarized. Its SHA-256 checksum will still be verified.\n' "$context" >&2
374
- elif [ "$ALLOW_UNSIGNED_INSTALL" = "true" ]; then
375
- printf 'WARNING: %s declares unsigned-override security metadata outside the alpha channel.\n' "$context" >&2
376
- else
377
- fail "$context is unsigned and is not an alpha release; refusing to install it"
378
- fi
379
- RELEASE_MODE="unsigned-override"
380
- ;;
381
- *) fail "$context does not contain valid signed-notarized release security metadata" ;;
382
- esac
383
- }
384
-
385
- # Test-only seam. Signature verification runs /usr/bin/codesign by absolute path
386
- # so nothing on PATH can impersonate it; the installer test suite substitutes a
387
- # stub, and like every other FREECODE_TEST_* override it is refused unless the
388
- # release is being read from a local file:// URL.
389
- resolve_codesign_bin() {
390
- [ -n "${FREECODE_TEST_CODESIGN_BIN:-}" ] || return 0
391
- case "$DOWNLOAD_BASE_URL" in
392
- file://*) CODESIGN_BIN="$FREECODE_TEST_CODESIGN_BIN" ;;
393
- *) fail "the test codesign override is permitted only with local file URLs" ;;
394
- esac
395
- }
396
-
397
- verify_release_signatures() {
398
- root=$1
399
- if [ "$RELEASE_MODE" = "unsigned-override" ]; then
400
- printf 'WARNING: skipping Apple signature and Team ID verification for this explicitly marked unsigned release.\n' >&2
401
- return
402
- fi
403
-
404
- [ -x "$CODESIGN_BIN" ] || fail "$CODESIGN_BIN is required to verify this release"
405
- for executable in \
406
- "$root/$LAUNCHER_RELATIVE_PATH" \
407
- "$root/$INTERFACE_RELATIVE_PATH" \
408
- "$root/$ENGINE_RELATIVE_PATH"
409
- do
410
- # Without -R this only proves the seal is intact, not who signed it.
411
- "$CODESIGN_BIN" --verify --strict --verbose=2 -R "$RELEASE_CODE_REQUIREMENT" "$executable" \
412
- || fail "Apple signature verification failed for $executable; it must be Developer ID signed by Apple team $EXPECTED_TEAM_IDENTIFIER"
413
- done
414
- }
415
-
416
- write_release_public_key() {
417
- destination=$1
418
- if [ -n "${FREECODE_TEST_RELEASE_PUBLIC_KEY_FILE:-}" ]; then
419
- case "$DOWNLOAD_BASE_URL" in
420
- file://*) cp "$FREECODE_TEST_RELEASE_PUBLIC_KEY_FILE" "$destination" ;;
421
- *) fail "the test release key override is permitted only with local file URLs" ;;
422
- esac
423
- return
424
- fi
425
- cat > "$destination" <<'EOF'
426
- -----BEGIN PUBLIC KEY-----
427
- MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA8so4sWArOV0GXihYvoSo
428
- xDLI1yMoILnVfCjYQwe4CKQ9EC10JV30cntMJZWXvXZS6STqQGIjyWisa6Op4nId
429
- OuXKDjaEcQIeftgZBzzyGrMmAMC3W57VAi3d+w/F2XnkXBaH3NOphfftF6Fxt3Q0
430
- 2CxN3PjuuFaOGytKsYF5uDLCvEb+MpzJlD3HZoBzhhMCAoY6wmCdlgaDxD20UFdy
431
- EigF5wwpcUWpYmbYMFp+6SLe/Fi8IrYirwiCv5eLur6PzFFcwFrJgUeZAdxmGuM8
432
- eJzZlc7mK0+gzi8KpC2FZ9eOx1SafPE4An4+u8rZ7WA1Qk5eCAhNU9lm+JAeeKcC
433
- XzhQpPUafybJg75dIbIayOC3tf5mn+X+Vl5+uY19nT2qcJsjgUeIcqbMs9FGXiIA
434
- 2G292N5WppyiDcx8yAIG6rOrAUGzHMYCrhvxQLAx2KP5QfC53VZ3La7yZOATY5ic
435
- F78qSSBrKDrT42vinbosbX++Wr34YB6pXhiZF6jHPHrxAgMBAAE=
436
- -----END PUBLIC KEY-----
437
- EOF
438
- }
439
-
440
- authenticate_release_manifest() {
441
- manifest_path=$1
442
- signature_path=$2
443
- context=$3
444
- if [ -s "$signature_path" ] && \
445
- /usr/bin/openssl dgst -sha256 \
446
- -verify "$RELEASE_PUBLIC_KEY" \
447
- -signature "$signature_path" \
448
- "$manifest_path" >/dev/null 2>&1
449
- then
450
- return
451
- fi
452
-
453
- manifest_sha=$(shasum -a 256 "$manifest_path" | awk '{ print $1 }')
454
- if [ "$manifest_sha" = "$LEGACY_ALPHA6_MANIFEST_SHA256" ]; then
455
- printf 'WARNING: %s predates release-manifest signatures; accepting its exact pinned alpha.6 manifest.\n' "$context" >&2
456
- return
457
- fi
458
- fail "$context does not have a valid Free Code release signature"
459
- }
460
-
461
- manifest_asset_value() {
462
- manifest_path=$1
463
- target=$2
464
- field=$3
465
- awk -v target="$target" -v field="$field" '
466
- /^[[:space:]]*"assets"[[:space:]]*:[[:space:]]*{/ { in_assets = 1; next }
467
- in_assets && index($0, "\"" target "\"") && index($0, ":") { in_target = 1; next }
468
- in_target && index($0, "\"" field "\"") {
469
- value = $0
470
- sub(/^[^:]*:[[:space:]]*/, "", value)
471
- sub(/,[[:space:]]*$/, "", value)
472
- sub(/^"/, "", value)
473
- sub(/"$/, "", value)
474
- print value
475
- exit
476
- }
477
- in_target && /^[[:space:]]*}[,]?[[:space:]]*$/ { exit }
478
- ' "$manifest_path"
479
- }
480
-
481
- DOWNLOAD_BASE_URL=${DOWNLOAD_BASE_URL%/}
482
- CHANNEL_BASE_URL=${CHANNEL_BASE_URL%/}
483
- validate_channel "$CHANNEL"
484
-
485
- resolve_codesign_bin
486
-
487
- TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/freecode-install.XXXXXX")
488
- RELEASE_PUBLIC_KEY="$TEMP_DIR/release-signing-public.pem"
489
- write_release_public_key "$RELEASE_PUBLIC_KEY"
490
-
491
- if [ -z "$VERSION" ]; then
492
- CHANNEL_MANIFEST="$TEMP_DIR/channel.json"
493
- CHANNEL_SIGNATURE="$TEMP_DIR/channel.json.sig"
494
- CHANNEL_URL="$CHANNEL_BASE_URL/$CHANNEL.json"
495
- printf 'Resolving Free Code channel %s...\n' "$CHANNEL"
496
- curl -fsSL --retry 3 --connect-timeout 15 "$CHANNEL_URL" -o "$CHANNEL_MANIFEST" \
497
- || fail "could not download channel manifest: $CHANNEL_URL"
498
- curl -fsSL --retry 3 --connect-timeout 15 "$CHANNEL_URL.sig" -o "$CHANNEL_SIGNATURE" 2>/dev/null \
499
- || rm -f "$CHANNEL_SIGNATURE"
500
- authenticate_release_manifest "$CHANNEL_MANIFEST" "$CHANNEL_SIGNATURE" "channel $CHANNEL"
501
- VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$CHANNEL_MANIFEST" | head -n 1)
502
- [ -n "$VERSION" ] || fail "channel manifest does not contain a version"
503
- validate_release_security "$CHANNEL_MANIFEST" "channel $CHANNEL"
504
- fi
505
- validate_version "$VERSION"
506
-
507
- mkdir -p "$INSTALL_ROOT"
508
- acquire_install_lock
509
-
510
- ASSET="freecode-darwin-$ARCH.tar.gz"
511
- TAG="v$VERSION"
512
- RELEASE_URL="$DOWNLOAD_BASE_URL/$TAG"
513
- ARCHIVE="$TEMP_DIR/$ASSET"
514
- RELEASE_MANIFEST="$TEMP_DIR/release-manifest.json"
515
- RELEASE_SIGNATURE="$TEMP_DIR/release-manifest.json.sig"
516
-
517
- curl -fsSL --retry 3 --connect-timeout 15 "$RELEASE_URL/release-manifest.json" -o "$RELEASE_MANIFEST" \
518
- || fail "could not download release manifest: $RELEASE_URL/release-manifest.json"
519
- curl -fsSL --retry 3 --connect-timeout 15 "$RELEASE_URL/release-manifest.json.sig" -o "$RELEASE_SIGNATURE" 2>/dev/null \
520
- || rm -f "$RELEASE_SIGNATURE"
521
- authenticate_release_manifest "$RELEASE_MANIFEST" "$RELEASE_SIGNATURE" "release $VERSION"
522
-
523
- MANIFEST_VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$RELEASE_MANIFEST" | head -n 1)
524
- [ "$MANIFEST_VERSION" = "$VERSION" ] \
525
- || fail "release manifest version ${MANIFEST_VERSION:-missing} does not match requested version $VERSION"
526
- validate_release_security "$RELEASE_MANIFEST" "release $VERSION"
527
-
528
- MANIFEST_TARGET="darwin-$ARCH"
529
- MANIFEST_ARCHIVE=$(manifest_asset_value "$RELEASE_MANIFEST" "$MANIFEST_TARGET" archive)
530
- [ "$MANIFEST_ARCHIVE" = "$ASSET" ] \
531
- || fail "release manifest does not declare the expected archive $ASSET"
532
- EXPECTED_SHA=$(manifest_asset_value "$RELEASE_MANIFEST" "$MANIFEST_TARGET" sha256)
533
- printf '%s\n' "$EXPECTED_SHA" | grep -Eq '^[0-9a-fA-F]{64}$' \
534
- || fail "release checksum is not a SHA-256 digest"
535
- EXPECTED_SIZE=$(manifest_asset_value "$RELEASE_MANIFEST" "$MANIFEST_TARGET" size)
536
- printf '%s\n' "$EXPECTED_SIZE" | grep -Eq '^[1-9][0-9]*$' \
537
- || fail "release manifest does not contain a valid archive size"
538
-
539
- printf 'Downloading Free Code %s for darwin-%s...\n' "$VERSION" "$ARCH"
540
- curl -fsSL --retry 3 --connect-timeout 15 "$RELEASE_URL/$ASSET" -o "$ARCHIVE" \
541
- || fail "could not download release asset: $RELEASE_URL/$ASSET"
542
- ACTUAL_SIZE=$(wc -c < "$ARCHIVE" | tr -d '[:space:]')
543
- [ "$ACTUAL_SIZE" = "$EXPECTED_SIZE" ] \
544
- || fail "download size verification failed for $ASSET"
545
- ACTUAL_SHA=$(shasum -a 256 "$ARCHIVE" | awk '{ print $1 }')
546
- [ "$ACTUAL_SHA" = "$EXPECTED_SHA" ] \
547
- || fail "checksum verification failed for $ASSET"
548
-
549
- if tar -tzf "$ARCHIVE" | grep -Eq '(^/|(^|/)\.\.(/|$))'; then
550
- fail "release archive contains an unsafe path"
551
- fi
552
-
553
- mkdir -p "$INSTALL_ROOT/versions" "$BIN_DIR"
554
- VERSION_DIR="$INSTALL_ROOT/versions/$VERSION"
555
- NEW_INSTALL="false"
556
-
557
- if [ ! -d "$VERSION_DIR" ]; then
558
- STAGE_DIR=$(mktemp -d "$INSTALL_ROOT/.install-$VERSION.XXXXXX")
559
- tar -xzf "$ARCHIVE" -C "$STAGE_DIR"
560
- [ -x "$STAGE_DIR/$LAUNCHER_RELATIVE_PATH" ] \
561
- || fail "release archive is missing executable $LAUNCHER_RELATIVE_PATH"
562
- [ -x "$STAGE_DIR/$INTERFACE_RELATIVE_PATH" ] \
563
- || fail "release archive is missing $INTERFACE_RELATIVE_PATH"
564
- [ -x "$STAGE_DIR/$ENGINE_RELATIVE_PATH" ] \
565
- || fail "release archive is missing $ENGINE_RELATIVE_PATH"
566
- verify_release_signatures "$STAGE_DIR"
567
- mv "$STAGE_DIR" "$VERSION_DIR"
568
- STAGE_DIR=""
569
- NEW_INSTALL="true"
570
- else
571
- [ -x "$VERSION_DIR/$LAUNCHER_RELATIVE_PATH" ] \
572
- || fail "existing install is incomplete: $VERSION_DIR"
573
- [ -x "$VERSION_DIR/$INTERFACE_RELATIVE_PATH" ] \
574
- || fail "existing install is incomplete: $VERSION_DIR/$INTERFACE_RELATIVE_PATH"
575
- [ -x "$VERSION_DIR/$ENGINE_RELATIVE_PATH" ] \
576
- || fail "existing install is incomplete: $VERSION_DIR/$ENGINE_RELATIVE_PATH"
577
- verify_release_signatures "$VERSION_DIR"
578
- fi
579
-
580
- PREVIOUS_LINK=""
581
- if [ -L "$BIN_DIR/freecode" ]; then
582
- PREVIOUS_LINK=$(readlink "$BIN_DIR/freecode")
583
- elif [ -e "$BIN_DIR/freecode" ]; then
584
- fail "refusing to replace non-symlink executable: $BIN_DIR/freecode"
585
- fi
586
- LINK_TEMP="$BIN_DIR/.freecode-link.$$"
587
- rm -f "$LINK_TEMP"
588
- ln -s "$VERSION_DIR/$LAUNCHER_RELATIVE_PATH" "$LINK_TEMP"
589
- mv -f "$LINK_TEMP" "$BIN_DIR/freecode"
590
-
591
- rollback_activation() {
592
- rm -f "$BIN_DIR/freecode"
593
- if [ -n "$PREVIOUS_LINK" ]; then
594
- ln -s "$PREVIOUS_LINK" "$BIN_DIR/freecode"
595
- fi
596
- if [ "$NEW_INSTALL" = "true" ]; then
597
- rm -rf "$VERSION_DIR"
598
- fi
599
- }
600
-
601
- printf 'Verifying Free Code %s...\n' "$VERSION"
602
- # Runtime diagnostics (such as Bun's AVX warning under Rosetta) go to stderr;
603
- # only stdout carries the version, and it must match exactly.
604
- VERSION_STDERR="$TEMP_DIR/version.stderr"
605
- VERSION_OUTPUT=$("$BIN_DIR/freecode" --version 2>"$VERSION_STDERR") || {
606
- cat "$VERSION_STDERR" >&2
607
- rollback_activation
608
- fail "freecode --version failed; restored the previous install"
609
- }
610
- cat "$VERSION_STDERR" >&2
611
- printf '%s\n' "$VERSION_OUTPUT"
612
- [ "$VERSION_OUTPUT" = "freecode-sh $VERSION" ] || {
613
- rollback_activation
614
- fail "freecode --version reported the wrong release; restored the previous install"
615
- }
616
- if ! "$BIN_DIR/freecode" doctor; then
617
- rollback_activation
618
- fail "freecode doctor failed; restored the previous install"
619
- fi
620
-
621
- rm -f "$INSTALL_ROOT/current-version"
622
- printf '%s\n' "$BIN_DIR" > "$INSTALL_ROOT/bin-dir"
623
- printf '%s\n' "$CHANNEL" > "$INSTALL_ROOT/channel"
624
- printf '%s\n' "$DOWNLOAD_BASE_URL" > "$INSTALL_ROOT/release-base-url"
625
- printf '%s\n' "$CHANNEL_BASE_URL" > "$INSTALL_ROOT/channel-base-url"
626
-
627
- release_install_lock
628
-
629
- printf '\nFree Code %s installed successfully.\n' "$VERSION"
630
- printf 'Executable: %s/freecode\n' "$BIN_DIR"
631
-
632
- if [ "$EMBEDDED_INSTALL" = "true" ]; then
633
- exit 0
634
- fi
635
-
636
- configure_path
637
-
638
- if [ "$PATH_CONFIGURED" = "true" ]; then
639
- printf 'Added Free Code to your PATH in %s.\n' "$PATH_PROFILE"
640
- else
641
- printf '\nFree Code could not update your shell profile automatically.\n'
642
- printf 'Add this line to your shell profile for future Terminal windows:\n'
643
- printf ' export PATH="%s:$PATH"\n' "$BIN_DIR"
644
- fi
645
-
646
- printf '\nVerify the install:\n'
647
- printf ' %s/freecode --version\n' "$BIN_DIR"
648
- printf ' %s/freecode doctor\n' "$BIN_DIR"
649
- printf '\nUninstall:\n'
650
- printf ' rm -f "%s/freecode" && rm -rf "%s"\n' "$BIN_DIR" "$INSTALL_ROOT"
651
-
652
- if [ "$LAUNCH_AFTER_INSTALL" = "true" ]; then
653
- if [ -n "${FREECODE_TEST_TTY_FILE:-}" ]; then
654
- if [ -r "$FREECODE_TEST_TTY_FILE" ]; then
655
- CAN_LAUNCH="true"
656
- else
657
- CAN_LAUNCH="false"
658
- fi
659
- elif [ -t 1 ]; then
660
- CAN_LAUNCH="true"
661
- else
662
- CAN_LAUNCH="false"
663
- fi
664
-
665
- if [ "$CAN_LAUNCH" = "true" ]; then
666
- printf '\nStarting Free Code...\n'
667
- if [ -n "${FREECODE_TEST_TTY_FILE:-}" ]; then
668
- if "$BIN_DIR/freecode" < "$FREECODE_TEST_TTY_FILE"; then
669
- LAUNCH_SUCCEEDED="true"
670
- else
671
- LAUNCH_SUCCEEDED="false"
672
- fi
673
- elif "$BIN_DIR/freecode" 0<&1; then
674
- LAUNCH_SUCCEEDED="true"
675
- else
676
- LAUNCH_SUCCEEDED="false"
677
- fi
678
-
679
- if [ "$LAUNCH_SUCCEEDED" != "true" ]; then
680
- printf '\nFree Code could not start automatically, but the installation succeeded.\n' >&2
681
- printf 'Open a Terminal and run:\n "%s/freecode"\n' "$BIN_DIR" >&2
682
- fi
683
- else
684
- printf '\nFree Code is installed. Open a Terminal and run:\n'
685
- printf ' "%s/freecode"\n' "$BIN_DIR"
686
- fi
687
- fi
@@ -1,7 +0,0 @@
1
- export function withoutPowerShellModulePath(environment) {
2
- const clean = { ...environment }
3
- for (const key of Object.keys(clean)) {
4
- if (key.toLowerCase() === 'psmodulepath') delete clean[key]
5
- }
6
- return clean
7
- }