| @@ -6,6 +6,7 @@ REPO="${EXECUTOR_REPO:-RhysSullivan/executor}" · 5 unmodified lines |
| 6 | 6 | INSTALL_DIR="${EXECUTOR_INSTALL_DIR:-$HOME/.executor/bin}" |
| 7 | 7 | WEB_INSTALL_DIR="${EXECUTOR_WEB_INSTALL_DIR:-$HOME/.executor/runtime/web}" |
| 8 | 8 | RUNTIME_DIR="${EXECUTOR_RUNTIME_DIR:-$HOME/.executor/runtime}" |
| 9 | +BOOTSTRAP_ARCHIVE_FILENAME="executor-bootstrap-project.tar.gz" |
| 9 | 10 | REQUESTED_VERSION="${VERSION:-}" |
| 10 | 11 | RESOLVED_VERSION="" |
| 11 | 12 | BINARY_PATH="" |
| @@ -15,6 +16,9 @@ SCRIPT_FILE="${BASH_SOURCE[0]:-$0}" · 3 unmodified lines |
| 15 | 16 | SCRIPT_DIR="$(cd -- "$(dirname -- "${SCRIPT_FILE}")" >/dev/null 2>&1 && pwd)" |
| 16 | 17 | LOCAL_BINARY_CANDIDATE="$SCRIPT_DIR/dist/executor" |
| 17 | 18 | LOCAL_WEB_CANDIDATE="$SCRIPT_DIR/dist/release" |
| 19 | +LOCAL_BOOTSTRAP_CANDIDATE="$LOCAL_WEB_CANDIDATE/$BOOTSTRAP_ARCHIVE_FILENAME" |
| 20 | +CHECKSUMS_FILE="" |
| 21 | +CHECKSUMS_FETCHED="0" |
| 18 | 22 | |
| 19 | 23 | RED='\033[0;31m' |
| 20 | 24 | MUTED='\033[0;2m' |
| @@ -38,6 +42,7 @@ Environment: · 17 unmodified lines |
| 38 | 42 | EXECUTOR_REPO GitHub repository to install from (default: RhysSullivan/executor) |
| 39 | 43 | EXECUTOR_INSTALL_DIR Install directory (default: ~/.executor/bin) |
| 40 | 44 | EXECUTOR_WEB_INSTALL_DIR Web bundle directory (default: ~/.executor/runtime/web) |
| 45 | + EXECUTOR_BOOTSTRAP_PROJECT_ARCHIVE Optional local bootstrap archive override |
| 41 | 46 | EOF |
| 42 | 47 | } |
| 43 | 48 | |
| @@ -112,9 +117,97 @@ SERVICE_DIR="$RUNTIME_DIR/services" · 68 unmodified lines |
| 112 | 117 | LOG_DIR="$RUNTIME_DIR/logs" |
| 113 | 118 | BACKEND_PID_FILE="$SERVICE_DIR/backend.pid" |
| 114 | 119 | WEB_PID_FILE="$SERVICE_DIR/web.pid" |
| 120 | +WORK_DIR="$(mktemp -d)" |
| 121 | +trap 'rm -rf "$WORK_DIR"' EXIT |
| 115 | 122 | mkdir -p "$INSTALL_DIR" |
| 116 | 123 | mkdir -p "$WEB_INSTALL_DIR" |
| 117 | 124 | |
| 125 | +require_command() { |
| 126 | + local command_name="$1" |
| 127 | + local install_hint="$2" |
| 128 | + if command -v "$command_name" >/dev/null 2>&1; then |
| 129 | + return |
| 130 | + fi |
| 131 | + |
| 132 | + echo -e "${RED}Missing required dependency: ${command_name}${NC}" |
| 133 | + echo "Install hint: ${install_hint}" |
| 134 | + exit 1 |
| 135 | +} |
| 136 | + |
| 137 | +sha256_of_file() { |
| 138 | + local target="$1" |
| 139 | + if command -v sha256sum >/dev/null 2>&1; then |
| 140 | + sha256sum "$target" | awk '{print $1}' |
| 141 | + return |
| 142 | + fi |
| 143 | + |
| 144 | + if command -v shasum >/dev/null 2>&1; then |
| 145 | + shasum -a 256 "$target" | awk '{print $1}' |
| 146 | + return |
| 147 | + fi |
| 148 | + |
| 149 | + echo -e "${RED}Missing checksum utility (sha256sum or shasum)${NC}" |
| 150 | + exit 1 |
| 151 | +} |
| 152 | + |
| 153 | +ensure_install_prerequisites() { |
| 154 | + require_command "curl" "Install curl from your OS package manager." |
| 155 | + require_command "tar" "Install tar from your OS package manager." |
| 156 | + if command -v sha256sum >/dev/null 2>&1 || command -v shasum >/dev/null 2>&1; then |
| 157 | + return |
| 158 | + fi |
| 159 | + |
| 160 | + echo -e "${RED}Missing required dependency: sha256sum or shasum${NC}" |
| 161 | + echo "Install hint: coreutils (sha256sum) or perl-Digest-SHA tools (shasum)." |
| 162 | + exit 1 |
| 163 | +} |
| 164 | + |
| 165 | +download_release_checksums() { |
| 166 | + if [[ "$CHECKSUMS_FETCHED" == "1" ]]; then |
| 167 | + return |
| 168 | + fi |
| 169 | + |
| 170 | + CHECKSUMS_FETCHED="1" |
| 171 | + local checksums_url |
| 172 | + if [[ -z "$REQUESTED_VERSION" ]]; then |
| 173 | + checksums_url="https://github.com/${REPO}/releases/latest/download/checksums.txt" |
| 174 | + else |
| 175 | + checksums_url="https://github.com/${REPO}/releases/download/v${RESOLVED_VERSION}/checksums.txt" |
| 176 | + fi |
| 177 | + |
| 178 | + local destination="$WORK_DIR/checksums.txt" |
| 179 | + if curl -fsSL "$checksums_url" -o "$destination"; then |
| 180 | + CHECKSUMS_FILE="$destination" |
| 181 | + fi |
| 182 | +} |
| 183 | + |
| 184 | +verify_release_checksum_if_available() { |
| 185 | + local downloaded_file="$1" |
| 186 | + local artifact_name="$2" |
| 187 | + download_release_checksums |
| 188 | + |
| 189 | + if [[ -z "$CHECKSUMS_FILE" || ! -f "$CHECKSUMS_FILE" ]]; then |
| 190 | + return |
| 191 | + fi |
| 192 | + |
| 193 | + local expected |
| 194 | + expected="$(grep -E "[[:xdigit:]]+[[:space:]]+${artifact_name}$" "$CHECKSUMS_FILE" | awk '{print $1}' | head -n1)" |
| 195 | + if [[ -z "$expected" ]]; then |
| 196 | + return |
| 197 | + fi |
| 198 | + |
| 199 | + local actual |
| 200 | + actual="$(sha256_of_file "$downloaded_file")" |
| 201 | + if [[ "$actual" != "$expected" ]]; then |
| 202 | + echo -e "${RED}Checksum mismatch for ${artifact_name}${NC}" |
| 203 | + echo " expected: ${expected}" |
| 204 | + echo " actual: ${actual}" |
| 205 | + exit 1 |
| 206 | + fi |
| 207 | +} |
| 208 | + |
| 209 | +ensure_install_prerequisites |
| 210 | + |
| 118 | 211 | install_local_binary() { |
| 119 | 212 | local src="$1" |
| 120 | 213 | local tmp_target |
| @@ -151,8 +244,7 @@ install_web_bundle_from_archive() { · 30 unmodified lines |
| 151 | 244 | |
| 152 | 245 | install_web_bundle() { |
| 153 | 246 | local web_url |
| 154 | | − local temp_archive |
| 155 | | − temp_archive="$(mktemp)" |
| 247 | + local temp_archive="$WORK_DIR/$web_filename" |
| 156 | 248 | |
| 157 | 249 | if [[ -z "$REQUESTED_VERSION" ]]; then |
| 158 | 250 | web_url="https://github.com/${REPO}/releases/latest/download/${web_filename}" |
| @@ -161,12 +253,11 @@ install_web_bundle() { · 2 unmodified lines |
| 161 | 253 | fi |
| 162 | 254 | |
| 163 | 255 | if curl -fsSL "$web_url" -o "$temp_archive"; then |
| 256 | + verify_release_checksum_if_available "$temp_archive" "$web_filename" |
| 164 | 257 | echo -e "${MUTED}Installing web bundle${NC} ${web_filename}" |
| 165 | 258 | install_web_bundle_from_archive "$temp_archive" |
| 166 | | − rm -f "$temp_archive" |
| 167 | 259 | return |
| 168 | 260 | fi |
| 169 | | − rm -f "$temp_archive" |
| 170 | 261 | |
| 171 | 262 | local local_web_archive="$LOCAL_WEB_CANDIDATE/${web_filename}" |
| 172 | 263 | if [[ -f "$local_web_archive" ]]; then |
| @@ -200,8 +291,7 @@ else · 27 unmodified lines |
| 200 | 291 | url="https://github.com/${REPO}/releases/download/v${version}/${filename}" |
| 201 | 292 | fi |
| 202 | 293 | |
| 203 | | − tmp_dir="$(mktemp -d)" |
| 204 | | − trap 'rm -rf "$tmp_dir"' EXIT |
| 294 | + tmp_dir="$WORK_DIR" |
| 205 | 295 | |
| 206 | 296 | echo -e "${MUTED}Installing ${APP}${NC} ${version:-latest}" |
| 207 | 297 | if ! curl -fsSL "$url" -o "$tmp_dir/$filename"; then |
| @@ -217,6 +307,7 @@ else · 9 unmodified lines |
| 217 | 307 | exit 1 |
| 218 | 308 | fi |
| 219 | 309 | else |
| 310 | + verify_release_checksum_if_available "$tmp_dir/$filename" "$filename" |
| 220 | 311 | tar -xzf "$tmp_dir/$filename" -C "$tmp_dir" |
| 221 | 312 | |
| 222 | 313 | if [[ ! -f "$tmp_dir/$APP" ]]; then |
| @@ -347,19 +438,24 @@ download_bootstrap_project_archive() { · 124 unmodified lines |
| 347 | 438 | return 1 |
| 348 | 439 | fi |
| 349 | 440 | |
| 350 | | − local urls=() |
| 351 | | − if [[ -n "$RESOLVED_VERSION" ]]; then |
| 352 | | − urls+=("https://github.com/${REPO}/archive/refs/tags/v${RESOLVED_VERSION}.tar.gz") |
| 353 | | − urls+=("https://github.com/${REPO}/archive/refs/tags/${RESOLVED_VERSION}.tar.gz") |
| 354 | | − fi |
| 355 | | − urls+=("https://github.com/${REPO}/archive/refs/heads/main.tar.gz") |
| 356 | | − |
| 357 | | − local url |
| 358 | | − for url in "${urls[@]}"; do |
| 359 | | − if curl -fsSL "$url" -o "$destination"; then |
| 441 | + if [[ -z "$REQUESTED_VERSION" ]]; then |
| 442 | + local latest_url="https://github.com/${REPO}/releases/latest/download/${BOOTSTRAP_ARCHIVE_FILENAME}" |
| 443 | + if curl -fsSL "$latest_url" -o "$destination"; then |
| 444 | + verify_release_checksum_if_available "$destination" "$BOOTSTRAP_ARCHIVE_FILENAME" |
| 360 | 445 | return 0 |
| 361 | 446 | fi |
| 362 | | − done |
| 447 | + else |
| 448 | + local versioned_url="https://github.com/${REPO}/releases/download/v${RESOLVED_VERSION}/${BOOTSTRAP_ARCHIVE_FILENAME}" |
| 449 | + if curl -fsSL "$versioned_url" -o "$destination"; then |
| 450 | + verify_release_checksum_if_available "$destination" "$BOOTSTRAP_ARCHIVE_FILENAME" |
| 451 | + return 0 |
| 452 | + fi |
| 453 | + fi |
| 454 | + |
| 455 | + if [[ -f "$LOCAL_BOOTSTRAP_CANDIDATE" ]]; then |
| 456 | + cp "$LOCAL_BOOTSTRAP_CANDIDATE" "$destination" |
| 457 | + return 0 |
| 458 | + fi |
| 363 | 459 | |
| 364 | 460 | return 1 |
| 365 | 461 | } |
| @@ -367,7 +463,7 @@ download_bootstrap_project_archive() { · 1 unmodified line |
| 367 | 463 | prepare_release_bootstrap_project() { |
| 368 | 464 | local bootstrap_root="$RUNTIME_DIR/bootstrap-project" |
| 369 | 465 | local version_file="$bootstrap_root/.executor-bootstrap-version" |
| 370 | | − local cache_key="${RESOLVED_VERSION:-main}" |
| 466 | + local cache_key="${RESOLVED_VERSION:-latest}" |
| 371 | 467 | |
| 372 | 468 | if [[ -f "$version_file" ]] && [[ "$(cat "$version_file" 2>/dev/null || true)" == "$cache_key" ]]; then |
| 373 | 469 | if is_convex_project_dir "$bootstrap_root"; then |
| @@ -380,10 +476,8 @@ prepare_release_bootstrap_project() { · 6 unmodified lines |
| 380 | 476 | fi |
| 381 | 477 | fi |
| 382 | 478 | |
| 383 | | − local archive_file |
| 384 | | − archive_file="$(mktemp)" |
| 479 | + local archive_file="$WORK_DIR/$BOOTSTRAP_ARCHIVE_FILENAME" |
| 385 | 480 | if ! download_bootstrap_project_archive "$archive_file"; then |
| 386 | | − rm -f "$archive_file" |
| 387 | 481 | printf "" |
| 388 | 482 | return |
| 389 | 483 | fi |
| @@ -391,12 +485,10 @@ prepare_release_bootstrap_project() { · 1 unmodified line |
| 391 | 485 | rm -rf "$bootstrap_root" |
| 392 | 486 | mkdir -p "$bootstrap_root" |
| 393 | 487 | if ! tar -xzf "$archive_file" -C "$bootstrap_root" --strip-components=1; then |
| 394 | | − rm -f "$archive_file" |
| 395 | 488 | rm -rf "$bootstrap_root" |
| 396 | 489 | printf "" |
| 397 | 490 | return |
| 398 | 491 | fi |
| 399 | | − rm -f "$archive_file" |
| 400 | 492 | |
| 401 | 493 | local project_dir="$bootstrap_root" |
| 402 | 494 | if is_convex_project_dir "$bootstrap_root"; then |