#!/usr/bin/env bash # # build-bundle.sh -- package the whole repo into a single self-extracting # script, dist/automations-bundle.sh. Host that one file on a webserver (or a # public Gitea release) and run it on a target host -- no repo/git access # needed. # # Usage: # ./build-bundle.sh # bundle the committed tree at HEAD # ./build-bundle.sh v1.2.0 # bundle a specific tag/ref # ./build-bundle.sh worktree # bundle the current working tree # # (tracked + new files, minus ignored) # OUT=/tmp/x.sh ./build-bundle.sh # custom output path # # The payload excludes ignored files (dist/, .env, globals.env, # ssh-notify.conf, keys, backups) so no secrets are embedded. set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REF="${1:-HEAD}" OUT="${OUT:-$DIR/dist/automations-bundle.sh}" MARKER="__ARCHIVE_BELOW__" command -v git >/dev/null 2>&1 || { echo "git is required." >&2; exit 1; } mkdir -p "$(dirname "$OUT")" # --------------------------------------------------------------------------- # Build the repo tarball (gzipped), then base64-encode it. # --------------------------------------------------------------------------- make_tar() { case "$REF" in worktree|WORKTREE|.) # Tracked + untracked-but-not-ignored files = current state minus # ignored (secrets, dist/). Respects .gitignore via --exclude-standard. git -C "$DIR" ls-files -co --exclude-standard -z \ | tar -czf - -C "$DIR" --null -T - ;; *) # Clean tar of the committed tree at REF (ignored files never tracked). git -C "$DIR" archive --format=tar "$REF" | gzip -9 ;; esac } echo "Packaging repo ($REF)..." PAYLOAD="$(make_tar | base64 | tr -d '\n')" # --------------------------------------------------------------------------- # Emit the self-extracting stub, then the payload after the marker. # --------------------------------------------------------------------------- { cat <<'STUB' #!/bin/sh # # automations-bundle.sh -- self-extracting bundle of the automations repo. # Generated by build-bundle.sh. Download, then run (it can't extract from a # pipe -- it needs to read itself as a file): # # curl -fsSLO https://your-host/automations-bundle.sh # sh automations-bundle.sh # launcher wizard # sh automations-bundle.sh bash scripts/setup-host.sh # run a script # SSH_PORT=2222 sh automations-bundle.sh bash scripts/harden-jumphost.sh # # POSIX sh, not bash: a stock Alpine host has busybox ash and no bash, so a # bash stub would fail before it could install anything. It installs bash # itself (everything inside the bundle is bash) and then runs the launcher. # # Env: # INSTALL_DIR where to extract (default /opt/automations) # BUNDLE_KEEP 1 to keep the extracted repo (default), 0 to use a temp dir # and remove it after the command finishes set -eu : "${INSTALL_DIR:=/opt/automations}" : "${BUNDLE_KEEP:=1}" SELF="$0" if [ ! -f "$SELF" ]; then echo "[x] Run me as a downloaded file, not via a pipe:" >&2 echo " curl -fsSLO /automations-bundle.sh && sh automations-bundle.sh" >&2 exit 1 fi # The repo contents -- launcher, scripts/, deployments/ -- are all bash. if ! command -v bash >/dev/null 2>&1; then echo "[+] bash not found; installing it..." >&2 if command -v apk >/dev/null 2>&1; then { apk add -q bash 2>/dev/null || { apk update -q && apk add -q bash; }; } || true elif command -v apt-get >/dev/null 2>&1; then { apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bash; } || true elif command -v dnf >/dev/null 2>&1; then dnf install -y -q bash || true elif command -v yum >/dev/null 2>&1; then yum install -y -q bash || true fi command -v bash >/dev/null 2>&1 || { echo "[x] bash is required, but it isn't installed and I couldn't install it automatically (need root + a supported package manager). Install bash, then re-run." >&2 exit 1 } fi if [ "$BUNDLE_KEEP" != "1" ]; then INSTALL_DIR="$(mktemp -d -t automations.XXXXXX)" trap 'rm -rf "$INSTALL_DIR"' EXIT fi echo "[+] Extracting bundle to $INSTALL_DIR ..." mkdir -p "$INSTALL_DIR" sed -e '1,/^__ARCHIVE_BELOW__$/d' "$SELF" | base64 -d | tar -xz -C "$INSTALL_DIR" cd "$INSTALL_DIR" chmod +x automations.sh build-bundle.sh scripts/*.sh deployments/*/*.sh 2>/dev/null || true if [ "$#" -gt 0 ]; then exec "$@" else exec bash ./automations.sh fi __ARCHIVE_BELOW__ STUB printf '%s\n' "$PAYLOAD" } > "$OUT" chmod +x "$OUT" echo "Built $OUT ($(wc -c < "$OUT") bytes) from $REF"