#!/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' #!/usr/bin/env bash # # 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 # bash automations-bundle.sh # launcher wizard # bash automations-bundle.sh bash scripts/setup-host.sh # run a script # SSH_PORT=2222 bash automations-bundle.sh bash scripts/harden-jumphost.sh # # 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 -euo pipefail : "${INSTALL_DIR:=/opt/automations}" : "${BUNDLE_KEEP:=1}" SELF="${BASH_SOURCE[0]:-$0}" if [[ ! -f "$SELF" ]]; then echo "[x] Run me as a downloaded file, not via a pipe:" >&2 echo " curl -fsSLO /automations-bundle.sh && bash automations-bundle.sh" >&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"