Repository navigation
Expand file tree
/
Copy pathget
More file actions
225 lines (209 loc) · 11.2 KB
/
Copy pathget
File metadata and controls
225 lines (209 loc) · 11.2 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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/bin/sh
# =============================================================================
# xerj installer · curl -fsSL https://xerj.org/get | sh
# =============================================================================
# Detects your OS + CPU, downloads the matching release binary from
# github.com/xerj-org/xerj/releases, verifies its SHA-256, and installs a
# single `xerj` binary. No JVM, no dependencies — one static binary.
#
# Environment overrides:
# XERJ_VERSION=v0.1.0 install a specific tag (default: latest release)
# XERJ_INSTALL_DIR=/path install location (default: ~/.local/bin, else ./ )
# XERJ_REPO=owner/name source repo (default: xerj-org/xerj)
# XERJ_LIBC=gnu|musl Linux C library (default: musl — see below)
# XERJ_INSECURE_SKIP_CHECKSUM=1
# install without verifying SHA-256. Only useful on a
# box with no hashing tool at all; you are choosing to
# run an unverified binary.
# =============================================================================
set -eu
REPO="${XERJ_REPO:-xerj-org/xerj}"
BOLD=''; DIM=''; RED=''; GRN=''; YEL=''; RST=''
if [ -t 1 ]; then
BOLD=$(printf '\033[1m'); DIM=$(printf '\033[2m'); RED=$(printf '\033[31m')
GRN=$(printf '\033[32m'); YEL=$(printf '\033[33m'); RST=$(printf '\033[0m')
fi
say() { printf '%s\n' "$*"; }
info() { printf '%s==>%s %s\n' "$YEL" "$RST" "$*"; }
ok() { printf '%s ok %s %s\n' "$GRN" "$RST" "$*"; }
die() { printf '%serror:%s %s\n' "$RED" "$RST" "$*" >&2; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || die "'$1' is required but not found in PATH"; }
need uname; need tar
# --- pick a downloader ------------------------------------------------------
if command -v curl >/dev/null 2>&1; then
DL() { curl -fsSL "$1" -o "$2"; }
DLS() { curl -fsSL "$1"; }
elif command -v wget >/dev/null 2>&1; then
DL() { wget -qO "$2" "$1"; }
DLS() { wget -qO - "$1"; }
else
die "need curl or wget"
fi
# --- detect target triple ---------------------------------------------------
# Supported matrix (8 release targets):
# Linux x86_64 / aarch64 (static musl build - any distro incl. Alpine)
# macOS x86_64 / arm64 (Apple Silicon)
# Windows x64 / ARM64 (via Git Bash / MSYS here; native PowerShell:
# powershell -ExecutionPolicy Bypass -c "irm https://xerj.org/get.ps1 | iex")
os=$(uname -s); arch=$(uname -m)
# Linux defaults to the fully-static musl build. That is a deliberate choice,
# not a fallback:
# * it runs on any distro including Alpine, with no glibc-version floor —
# a gnu binary linked against a newer glibc dies at exec on an older host
# ("GLIBC_2.xx not found"), which is an install failure we cannot see;
# * the usual objection to musl, its slow malloc, does not apply: xerj links
# jemalloc as the global allocator on every non-MSVC target
# (engine/crates/xerj-server/Cargo.toml).
# The gnu artifacts are still published and still supported — ask for them
# with XERJ_LIBC=gnu. (Before 2026-08-08 an unconditional override below made
# gnu unreachable from this script while the case arm still claimed gnu; the
# comment and the behaviour now agree.)
case "$os" in
Linux) plat="unknown-linux-musl"; ext="tar.gz"; bin="xerj" ;;
Darwin) plat="apple-darwin"; ext="tar.gz"; bin="xerj" ;;
MINGW*|MSYS*|CYGWIN*) plat="pc-windows-msvc"; ext="zip"; bin="xerj.exe" ;;
*) die "unsupported OS: $os (xerj ships Linux, macOS and Windows builds)" ;;
esac
case "$arch" in
x86_64|amd64) cpu="x86_64" ;;
aarch64|arm64) cpu="aarch64" ;;
*) die "unsupported CPU: $arch (xerj ships x86_64/amd64 and aarch64/arm64 builds for Linux, macOS and Windows)" ;;
esac
if [ "$os" = "Linux" ]; then
case "${XERJ_LIBC:-musl}" in
musl) ;; # the default set above
gnu) plat="unknown-linux-gnu" ;;
*) die "XERJ_LIBC must be 'gnu' or 'musl' (got '${XERJ_LIBC}')" ;;
esac
elif [ -n "${XERJ_LIBC:-}" ]; then
say "${YEL}note:${RST} XERJ_LIBC only applies to Linux — ignoring it on ${os}"
fi
target="${cpu}-${plat}"
# --- resolve version --------------------------------------------------------
if [ "${XERJ_VERSION:-}" = "" ]; then
info "resolving latest release of ${REPO}…"
# Follow the /releases/latest redirect to read the tag without jq.
tag=$(DLS "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1)
[ -n "$tag" ] || die "could not resolve latest release — set XERJ_VERSION=vX.Y.Z (see https://github.com/${REPO}/releases)"
else
tag="$XERJ_VERSION"
# Release tags are v-prefixed; accept XERJ_VERSION=1.0.0 too.
case "$tag" in v*) ;; *) tag="v$tag" ;; esac
fi
ver="${tag#v}"
info "installing xerj ${BOLD}${tag}${RST} for ${BOLD}${target}${RST}"
stage="xerj-${ver}-${target}"
asset="${stage}.${ext}"
base="https://github.com/${REPO}/releases/download/${tag}"
# The X-template form works on GNU, BSD/macOS, and busybox mktemp alike.
tmp=$(mktemp -d 2>/dev/null || mktemp -d "${TMPDIR:-/tmp}/xerj.XXXXXX")
trap 'rm -rf "$tmp"' EXIT INT TERM
info "downloading ${asset}…"
DL "${base}/${asset}" "${tmp}/${asset}" \
|| die "download failed for ${target}. Available assets: https://github.com/${REPO}/releases/tag/${tag}"
# --- verify checksum (fail closed — every release publishes the .sha256) ----
#
# DO NOT REORDER THE NEXT LINE BELOW THE TOOL SEARCH. The checksum is fetched
# unconditionally, before we look for a hashing tool, so every installer run
# downloads exactly one binary and exactly one matching .sha256. That 1:1
# pairing is the ONLY fingerprint that lets GitHub's per-asset downloa
A3FC
d counts
# be read as installer runs (see metrics/README.md); checking for the tool
# first would skip the fetch on some machines and silently destroy the only
# install metric this project has.
DL "${base}/${asset}.sha256" "${tmp}/${asset}.sha256" 2>/dev/null \
|| die "could not download checksum ${asset}.sha256 — refusing to install an unverified binary"
want=$(awk '{print $1}' "${tmp}/${asset}.sha256" | tr 'A-F' 'a-f')
[ "${#want}" -eq 64 ] || die "malformed ${asset}.sha256 (expected 64 hex chars) — refusing to install an unverified binary"
case "$want" in *[!0-9a-f]*) die "malformed ${asset}.sha256 (not hex) — refusing to install an unverified binary" ;; esac
# Try hard to find a SHA-256 implementation before giving up: coreutils,
# perl-shasum, openssl, busybox, and the BSD/macOS spellings.
got=''
if command -v sha256sum >/dev/null 2>&1; then got=$(sha256sum "${tmp}/${asset}" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then got=$(shasum -a 256 "${tmp}/${asset}" | awk '{print $1}')
elif command -v openssl >/dev/null 2>&1; then got=$(openssl dgst -sha256 "${tmp}/${asset}" | awk '{print $NF}')
elif command -v sha256 >/dev/null 2>&1; then got=$(sha256 -q "${tmp}/${asset}" | awk '{print $NF}')
elif command -v busybox >/dev/null 2>&1; then got=$(busybox sha256sum "${tmp}/${asset}" | awk '{print $1}')
elif command -v cksum >/dev/null 2>&1; then got=$(cksum -a sha256 "${tmp}/${asset}" 2>/dev/null | awk '{print $NF}')
fi
got=$(printf '%s' "$got" | tr 'A-F' 'a-f')
# Fail CLOSED. Until 2026-08-08 this branch printed a warning and installed
# anyway, which is not what "refusing to install an unverified binary" three
# lines up promises. If a machine genuinely has no hashing tool, the user can
# still opt in explicitly — but it has to be their decision, not ours.
if [ -z "$got" ]; then
if [ "${XERJ_INSECURE_SKIP_CHECKSUM:-}" = "1" ]; then
say "${YEL}warning:${RST} no SHA-256 tool found and XERJ_INSECURE_SKIP_CHECKSUM=1 —"
say "${YEL} ${RST} installing an ${BOLD}unverified${RST} binary at your request (expected ${want})"
else
die "no SHA-256 tool found (tried sha256sum, shasum, openssl, sha256, busybox, cksum)
— refusing to install an unverified binary.
Install coreutils or openssl and re-run, or verify by hand:
expected sha256: ${want}
source: ${base}/${asset}
To install anyway (not recommended): XERJ_INSECURE_SKIP_CHECKSUM=1"
fi
elif [ "$got" = "$want" ]; then
ok "sha256 verified"
else
die "sha256 mismatch (want $want, got $got)"
fi
# --- extract ----------------------------------------------------------------
info "extracting…"
if [ "$ext" = "zip" ]; then
# Git Bash / MSYS often lack `unzip` — fall back to the PowerShell that
# ships with every Windows box. Paths travel via environment variables
# (never interpolated into the PS command) and -LiteralPath, so spaces,
# apostrophes and [brackets] in %TEMP% can't break quoting or glob.
if command -v unzip >/dev/null 2>&1; then
unzip -q "${tmp}/${asset}" -d "$tmp"
elif command -v powershell.exe >/dev/null 2>&1; then
zip_w=$(cygpath -w "${tmp}/${asset}" 2>/dev/null || printf '%s' "${tmp}/${asset}")
dst_w=$(cygpath -w "$tmp" 2>/dev/null || printf '%s' "$tmp")
XERJ_ZIP="$zip_w" XERJ_DST="$dst_w" powershell.exe -NoProfile -NonInteractive -Command \
'Expand-Archive -LiteralPath $env:XERJ_ZIP -DestinationPath $env:XERJ_DST -Force' \
|| die "failed to extract ${asset} (install 'unzip', or use the native installer: powershell -ExecutionPolicy Bypass -c \"irm https://xerj.org/get.ps1 | iex\")"
else
die "need 'unzip' to extract ${asset} — or use the native installer: powershell -ExecutionPolicy Bypass -c \"irm https://xerj.org/get.ps1 | iex\""
fi
else
tar -xzf "${tmp}/${asset}" -C "$tmp"
fi
src="${tmp}/${stage}/${bin}"
[ -f "$src" ] || src=$(find "$tmp" -name "$bin" -type f | head -n1)
[ -f "$src" ] || die "binary '$bin' not found in archive"
chmod +x "$src" 2>/dev/null || true
# --- choose install dir -----------------------------------------------------
# Re-testing -w AFTER the mkdir matters: mkdir -p on an EXISTING but
# root-owned ~/.local/bin returns 0, and we must fall through to $(pwd)
# instead of failing later at the copy.
if [ "${XERJ_INSTALL_DIR:-}" != "" ]; then dir="$XERJ_INSTALL_DIR"
elif [ -n "${HOME:-}" ] \
&& { [ -d "$HOME/.local/bin" ] || mkdir -p "$HOME/.local/bin" 2>/dev/null; } \
&& [ -w "$HOME/.local/bin" ]; then dir="$HOME/.local/bin"
else dir="$(pwd)"; fi
mkdir -p "$dir" 2>/dev/null || die "cannot create install dir: $dir"
# Stage inside the destination directory, then rename: same-directory mv is
# an atomic rename(2), so $dest is always either the old or the new binary —
# never a truncated copy (mv from tmpfs /tmp is a non-atomic cross-device
# copy that Ctrl-C or ENOSPC could otherwise tear mid-write).
dest="${dir}/${bin}"
tmpdest="${dir}/.${bin}.tmp.$$"
if cp "$src" "$tmpdest" && mv -f "$tmpdest" "$dest"; then
ok "installed ${BOLD}${dest}${RST}"
else
rm -f "$tmpdest"
die "failed to install to $dest"
fi
# --- next steps -------------------------------------------------------------
say ""
say "${BOLD}xerj ${tag}${RST} is ready."
case ":${PATH}:" in
*":${dir}:"*) say " ${DIM}start it:${RST} xerj --insecure --data-dir ./data" ;;
*) say " ${DIM}${dir} is not on your PATH — either add it:${RST}"
say " export PATH=\"${dir}:\$PATH\""
say " ${DIM}or run it directly:${RST} ${dest} --insecure --data-dir ./data" ;;
esac
say ""
say " ${DIM}then open the console:${RST} http://localhost:9200/_xerj-console/"
say " ${DIM}docs:${RST} https://xerj.org/docs/ ${DIM}·${RST} ${DIM}source:${RST} https://github.com/${REPO}"