diff --git a/deploy/home-indexer/install.sh b/deploy/home-indexer/install.sh new file mode 100644 index 0000000..5e5478a --- /dev/null +++ b/deploy/home-indexer/install.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# host prereqs for running the typeahead snapshot builder (MODE=indexer) on the +# home box (heavypad) as a Prefect home-pool batch job. Installs the same Zig + +# rclone versions the Fly Dockerfile pins, to ~/.local/bin (on the worker PATH). +# Idempotent: re-running upgrades/repins in place. +set -euo pipefail + +ZIG_VERSION=0.16.0 +RCLONE_VERSION=1.69.1 +ARCH=x86_64 +BIN="$HOME/.local/bin" +OPT="$HOME/.local/opt" +mkdir -p "$BIN" "$OPT" + +# --- zig 0.16.0 (matches services/Dockerfile) --- +if [ "$("$BIN/zig" version 2>/dev/null || true)" != "$ZIG_VERSION" ]; then + echo "installing zig $ZIG_VERSION ..." + tmp=$(mktemp -d) + curl -fL "https://ziglang.org/download/${ZIG_VERSION}/zig-${ARCH}-linux-${ZIG_VERSION}.tar.xz" \ + | tar -xJ -C "$tmp" + rm -rf "$OPT/zig-${ARCH}-linux-${ZIG_VERSION}" + mv "$tmp/zig-${ARCH}-linux-${ZIG_VERSION}" "$OPT/" + ln -sf "$OPT/zig-${ARCH}-linux-${ZIG_VERSION}/zig" "$BIN/zig" + rm -rf "$tmp" +else + echo "zig $ZIG_VERSION already present" +fi + +# --- rclone 1.69.1 (matches services/Dockerfile; R2 multipart transport) --- +if [ "$("$BIN/rclone" version 2>/dev/null | head -1 | awk '{print $2}' || true)" != "v${RCLONE_VERSION}" ]; then + echo "installing rclone $RCLONE_VERSION ..." + tmp=$(mktemp -d) + curl -fsSL "https://downloads.rclone.org/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip" -o "$tmp/rclone.zip" + unzip -oj "$tmp/rclone.zip" 'rclone-*/rclone' -d "$BIN" + chmod +x "$BIN/rclone" + rm -rf "$tmp" +else + echo "rclone $RCLONE_VERSION already present" +fi + +echo "--- versions ---" +"$BIN/zig" version +"$BIN/rclone" version | head -1 +echo "done. ensure $BIN is on the worker PATH (it already is for the home-pool unit)." diff --git a/services/src/index/r2.zig b/services/src/index/r2.zig index fbc342b..46fdc81 100644 --- a/services/src/index/r2.zig +++ b/services/src/index/r2.zig @@ -22,9 +22,18 @@ const log = std.log.scoped(.r2); extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; extern "c" fn unsetenv(name: [*:0]const u8) c_int; -const RCLONE = "/usr/local/bin/rclone"; const CONFIG_PATH = "/tmp/rclone-r2.conf"; +/// rclone binary path, resolved at runtime so the host decides where rclone +/// lives — `INDEX_RCLONE_BIN` if set, else a bare "rclone" (PATH-searched). +/// The Fly image has it at /usr/local/bin; a home box may have it under +/// ~/.local/bin. Baking an absolute path here broke the home-box build, so +/// don't: let the env (or PATH) say where it is. +fn rcloneBin() []const u8 { + if (std.c.getenv("INDEX_RCLONE_BIN")) |p| return std.mem.span(p); + return "rclone"; +} + pub const Error = error{ MissingR2Config, RcloneFailed }; /// rclone remote handle: the bucket name + the path to the written config. @@ -104,7 +113,7 @@ pub fn upload(gpa: Allocator, cfg: Config, local_path: []const u8, object_key: [ const remote = try std.fmt.allocPrint(gpa, "r2:{s}/{s}", .{ cfg.bucket, object_key }); defer gpa.free(remote); log.info("rclone upload {s} -> {s}", .{ local_path, remote }); - try run(&.{ RCLONE, "--config", cfg.config_path, "copyto", local_path, remote }); + try run(&.{ rcloneBin(), "--config", cfg.config_path, "copyto", local_path, remote }); } /// Download r2:/ → local_path. @@ -112,7 +121,7 @@ pub fn download(gpa: Allocator, cfg: Config, object_key: []const u8, local_path: const remote = try std.fmt.allocPrint(gpa, "r2:{s}/{s}", .{ cfg.bucket, object_key }); defer gpa.free(remote); log.info("rclone download {s} -> {s}", .{ remote, local_path }); - try run(&.{ RCLONE, "--config", cfg.config_path, "copyto", remote, local_path }); + try run(&.{ rcloneBin(), "--config", cfg.config_path, "copyto", remote, local_path }); } const testing = std.testing;