Skip to content

Commit 47b170b

Browse files
committed
feat(installers): improve self-host installer UX
Add colorized output and helper logging functions (say/info/success/warn) with INSTALLER_COLOR_MODE support (auto|always|never). Introduce tar_extract_gz to filter noisy LIBARCHIVE tar warnings while preserving real errors, and use it when extracting archives (minisign and hstack). Replace plain echoes with structured info/success/warn messages, make checksum grep robust (avoid failing pipeline), and surface log paths and troubleshooting guidance when self-host install fails. Add a test (installers_self_host_tar_noise_and_guidance.test.mjs) to verify suppression of tar noise and that guidance is printed.
1 parent 8f90ed9 commit 47b170b

2 files changed

Lines changed: 337 additions & 11 deletions

File tree

scripts/release/installers/self-host.sh

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,61 @@ MINISIGN_PUBKEY="${HAPPIER_MINISIGN_PUBKEY:-${DEFAULT_MINISIGN_PUBKEY}}"
2222
MINISIGN_PUBKEY_URL="${HAPPIER_MINISIGN_PUBKEY_URL:-https://happier.dev/happier-release.pub}"
2323
MINISIGN_BIN="minisign"
2424

25+
INSTALLER_COLOR_MODE="${HAPPIER_INSTALLER_COLOR:-auto}" # auto|always|never
26+
27+
supports_color() {
28+
if [[ "${INSTALLER_COLOR_MODE}" == "never" ]]; then
29+
return 1
30+
fi
31+
if [[ -n "${NO_COLOR:-}" ]]; then
32+
return 1
33+
fi
34+
if [[ "${INSTALLER_COLOR_MODE}" == "always" ]]; then
35+
return 0
36+
fi
37+
[[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]
38+
}
39+
40+
if supports_color; then
41+
COLOR_RESET=$'\033[0m'
42+
COLOR_BOLD=$'\033[1m'
43+
COLOR_GREEN=$'\033[32m'
44+
COLOR_YELLOW=$'\033[33m'
45+
COLOR_CYAN=$'\033[36m'
46+
else
47+
COLOR_RESET=""
48+
COLOR_BOLD=""
49+
COLOR_GREEN=""
50+
COLOR_YELLOW=""
51+
COLOR_CYAN=""
52+
fi
53+
54+
say() {
55+
printf '%s\n' "$*"
56+
}
57+
58+
info() {
59+
say "${COLOR_CYAN}$*${COLOR_RESET}"
60+
}
61+
62+
success() {
63+
say "${COLOR_GREEN}$*${COLOR_RESET}"
64+
}
65+
66+
warn() {
67+
say "${COLOR_YELLOW}$*${COLOR_RESET}"
68+
}
69+
70+
tar_extract_gz() {
71+
local archive_path="$1"
72+
local dest_dir="$2"
73+
mkdir -p "${dest_dir}"
74+
# GNU tar on Linux emits noisy, non-actionable warnings when extracting archives created by bsdtar/libarchive:
75+
# "Ignoring unknown extended header keyword 'LIBARCHIVE.xattr...'"
76+
# Filter those while preserving real errors.
77+
tar -xzf "${archive_path}" -C "${dest_dir}" 2> >(grep -v -E "^tar: Ignoring unknown extended header keyword" >&2 || true)
78+
}
79+
2580
usage() {
2681
cat <<'EOF'
2782
Usage:
@@ -296,7 +351,7 @@ ensure_minisign() {
296351
local extract_dir="${TMP_DIR}/minisign-extract"
297352
mkdir -p "${extract_dir}"
298353
if [[ "${asset}" == *.tar.gz ]]; then
299-
tar -xzf "${archive_path}" -C "${extract_dir}"
354+
tar_extract_gz "${archive_path}" "${extract_dir}"
300355
else
301356
if command -v unzip >/dev/null 2>&1; then
302357
unzip -q "${archive_path}" -d "${extract_dir}"
@@ -345,7 +400,7 @@ write_minisign_public_key() {
345400
}
346401

347402
API_URL="https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${TAG}"
348-
echo "Fetching ${TAG} release metadata..."
403+
info "Fetching ${TAG} release metadata..."
349404
if ! RELEASE_JSON="$(curl -fsSL "${API_URL}")"; then
350405
if [[ "${CHANNEL}" == "stable" ]]; then
351406
echo "No stable releases found for Happier Stack." >&2
@@ -393,7 +448,7 @@ CHECKSUMS_PATH="${TMP_DIR}/checksums.txt"
393448
curl -fsSL "${ASSET_URL}" -o "${ARCHIVE_PATH}"
394449
curl -fsSL "${CHECKSUMS_URL}" -o "${CHECKSUMS_PATH}"
395450

396-
EXPECTED_SHA="$(grep -E " $(basename "${ASSET_URL}")$" "${CHECKSUMS_PATH}" | awk '{print $1}' | head -n 1)"
451+
EXPECTED_SHA="$(grep -E " $(basename "${ASSET_URL}")$" "${CHECKSUMS_PATH}" | awk '{print $1}' | head -n 1 || true)"
397452
if [[ -z "${EXPECTED_SHA}" ]]; then
398453
echo "Failed to resolve checksum for $(basename "${ASSET_URL}")" >&2
399454
exit 1
@@ -403,7 +458,7 @@ if [[ "${EXPECTED_SHA}" != "${ACTUAL_SHA}" ]]; then
403458
echo "Checksum verification failed." >&2
404459
exit 1
405460
fi
406-
echo "Checksum verified."
461+
success "Checksum verified."
407462

408463
if ! ensure_minisign; then
409464
echo "minisign is required for installer signature verification." >&2
@@ -416,11 +471,11 @@ SIG_PATH="${TMP_DIR}/checksums.txt.minisig"
416471
write_minisign_public_key "${PUBKEY_PATH}"
417472
curl -fsSL "${SIG_URL}" -o "${SIG_PATH}"
418473
"${MINISIGN_BIN}" -Vm "${CHECKSUMS_PATH}" -x "${SIG_PATH}" -p "${PUBKEY_PATH}" >/dev/null
419-
echo "Signature verified."
474+
success "Signature verified."
420475

421476
EXTRACT_DIR="${TMP_DIR}/extract"
422477
mkdir -p "${EXTRACT_DIR}"
423-
tar -xzf "${ARCHIVE_PATH}" -C "${EXTRACT_DIR}"
478+
tar_extract_gz "${ARCHIVE_PATH}" "${EXTRACT_DIR}"
424479
BINARY_PATH="$(find "${EXTRACT_DIR}" -type f -name hstack -perm -u+x | head -n 1 || true)"
425480
if [[ -z "${BINARY_PATH}" ]]; then
426481
echo "Failed to locate extracted hstack binary." >&2
@@ -432,7 +487,7 @@ cp "${BINARY_PATH}" "${STACK_INSTALL_DIR}/bin/hstack"
432487
chmod +x "${STACK_INSTALL_DIR}/bin/hstack"
433488
ln -sf "${STACK_INSTALL_DIR}/bin/hstack" "${STACK_BIN_DIR}/hstack"
434489

435-
echo "Installed hstack to ${STACK_INSTALL_DIR}/bin/hstack"
490+
success "Installed hstack to ${STACK_INSTALL_DIR}/bin/hstack"
436491

437492
SELF_HOST_ARGS=(self-host install --non-interactive --channel="${CHANNEL}" --mode="${MODE}")
438493
if [[ "${WITH_CLI}" != "1" ]]; then
@@ -442,10 +497,56 @@ fi
442497
export HAPPIER_NONINTERACTIVE="${NONINTERACTIVE}"
443498

444499
if [[ "${NONINTERACTIVE}" != "1" ]]; then
445-
echo "Starting Happier Self-Host guided installation..."
500+
info "Starting Happier Self-Host guided installation..."
501+
say
502+
info "This can take a few minutes. If it looks stuck, check logs:"
503+
if [[ "${MODE}" == "system" ]]; then
504+
SELF_HOST_LOG_DIR="${HAPPIER_SELF_HOST_LOG_DIR:-/var/log/happier}"
505+
SELF_HOST_SERVICE_NAME="${HAPPIER_SELF_HOST_SERVICE_NAME:-happier-server}"
506+
else
507+
SELF_HOST_LOG_DIR="${HAPPIER_SELF_HOST_LOG_DIR:-${HAPPIER_HOME}/self-host/logs}"
508+
SELF_HOST_SERVICE_NAME="${HAPPIER_SELF_HOST_SERVICE_NAME:-happier-server}"
509+
fi
510+
say " - ${SELF_HOST_LOG_DIR}/server.err.log"
511+
say " - ${SELF_HOST_LOG_DIR}/server.out.log"
512+
if [[ "${OS}" == "linux" ]]; then
513+
if [[ "${MODE}" == "system" ]]; then
514+
say " - sudo journalctl -u ${SELF_HOST_SERVICE_NAME} -e --no-pager"
515+
else
516+
say " - journalctl --user -u ${SELF_HOST_SERVICE_NAME} -e --no-pager"
517+
fi
518+
fi
519+
say
520+
fi
521+
if ! "${STACK_INSTALL_DIR}/bin/hstack" "${SELF_HOST_ARGS[@]}"; then
522+
warn
523+
warn "[self-host] install failed"
524+
say
525+
info "Troubleshooting:"
526+
say " ${STACK_BIN_DIR}/hstack self-host status --mode=${MODE} --channel=${CHANNEL}"
527+
say " ${STACK_BIN_DIR}/hstack self-host doctor --mode=${MODE} --channel=${CHANNEL}"
528+
say " ${STACK_BIN_DIR}/hstack self-host config view --mode=${MODE} --channel=${CHANNEL} --json"
529+
say
530+
info "Logs:"
531+
if [[ "${MODE}" == "system" ]]; then
532+
SELF_HOST_LOG_DIR="${HAPPIER_SELF_HOST_LOG_DIR:-/var/log/happier}"
533+
SELF_HOST_SERVICE_NAME="${HAPPIER_SELF_HOST_SERVICE_NAME:-happier-server}"
534+
else
535+
SELF_HOST_LOG_DIR="${HAPPIER_SELF_HOST_LOG_DIR:-${HAPPIER_HOME}/self-host/logs}"
536+
SELF_HOST_SERVICE_NAME="${HAPPIER_SELF_HOST_SERVICE_NAME:-happier-server}"
537+
fi
538+
say " tail -n 200 ${SELF_HOST_LOG_DIR}/server.err.log"
539+
say " tail -n 200 ${SELF_HOST_LOG_DIR}/server.out.log"
540+
if [[ "${OS}" == "linux" ]]; then
541+
if [[ "${MODE}" == "system" ]]; then
542+
say " sudo journalctl -u ${SELF_HOST_SERVICE_NAME} -e --no-pager"
543+
else
544+
say " journalctl --user -u ${SELF_HOST_SERVICE_NAME} -e --no-pager"
545+
fi
546+
fi
547+
exit 1
446548
fi
447-
"${STACK_INSTALL_DIR}/bin/hstack" "${SELF_HOST_ARGS[@]}"
448549

449550
echo
450-
echo "Happier Self-Host installation completed."
451-
echo "Run: ${STACK_BIN_DIR}/hstack self-host status"
551+
success "Happier Self-Host installation completed."
552+
info "Run: ${STACK_BIN_DIR}/hstack self-host status"

0 commit comments

Comments
 (0)