From 2466a80ce28add7b74ed192b6015f2a1110dc69e Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Thu, 18 Jun 2026 23:24:30 -0400 Subject: [PATCH] feat(cre): one-shot curl|bash installer for credential-rotation-enforcer - fresh-machine to 'cre' on PATH, runnable from a clone or piped from the web --- .../scripts/install.sh | 291 ++++++++++++++---- 1 file changed, 235 insertions(+), 56 deletions(-) diff --git a/PROJECTS/intermediate/credential-rotation-enforcer/scripts/install.sh b/PROJECTS/intermediate/credential-rotation-enforcer/scripts/install.sh index 74c74822..11bce68f 100755 --- a/PROJECTS/intermediate/credential-rotation-enforcer/scripts/install.sh +++ b/PROJECTS/intermediate/credential-rotation-enforcer/scripts/install.sh @@ -1,73 +1,252 @@ #!/usr/bin/env bash # ©AngelaMos | 2026 # install.sh +# +# One-shot installer for cre (Credential Rotation Enforcer). Goes from a fresh +# machine to `cre` on your PATH, whether run from a clone or piped from the web: +# +# curl -fsSL https://angelamos.com/cre/install.sh | bash set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local}" +# ============================================================================ +# Config +# ============================================================================ +REPO_OWNER="CarterPerez-dev" +REPO_NAME="Cybersecurity-Projects" +SUBDIR="PROJECTS/intermediate/credential-rotation-enforcer" +BINARY="cre" +REPO_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}.git" +DEFAULT_BRANCH="main" +MIN_CRYSTAL="1.20.0" -bold() { printf "\033[1m%s\033[0m\n" "$*"; } -green() { printf "\033[32m%s\033[0m\n" "$*"; } -yellow() { printf "\033[33m%s\033[0m\n" "$*"; } -red() { printf "\033[31m%s\033[0m\n" "$*"; } +INSTALL_DIR="${CRE_INSTALL_DIR:-$HOME/.local/bin}" -bold "Credential Rotation Enforcer - install.sh" - -# 1) Crystal -if ! command -v crystal >/dev/null 2>&1; then - yellow "Crystal not found. Install instructions: https://crystal-lang.org/install/" - yellow "Or, on Linux:" - yellow " curl -fsSL https://crystal-lang.org/install.sh | sudo bash" - yellow "Or, on macOS:" - yellow " brew install crystal" - exit 1 +# ============================================================================ +# Colors (gated so `| bash`, logs, and CI stay clean) +# ============================================================================ +if [ -t 2 ] && [ -z "${NO_COLOR:-}" ]; then + BOLD=$'\033[1m'; DIM=$'\033[2m'; RED=$'\033[31m'; GREEN=$'\033[32m' + YELLOW=$'\033[33m'; CYAN=$'\033[36m'; RESET=$'\033[0m' +else + BOLD=""; DIM=""; RED=""; GREEN=""; YELLOW=""; CYAN=""; RESET="" fi -crystal_version=$(crystal --version | head -1 | awk '{print $2}') -green "Crystal $crystal_version found" -# 2) System deps (libpcre2 for regex, libssl for crypto) -case "$(uname -s)" in - Linux) - if ! ldconfig -p 2>/dev/null | grep -q libpcre2-8; then - yellow "libpcre2 not detected. On Debian/Ubuntu: sudo apt-get install -y libpcre2-dev" - yellow "On Alpine: apk add pcre2-dev" - fi - ;; - Darwin) - : # macOS bundles what's needed - ;; +info() { printf '%s\n' " ${CYAN}+${RESET} $*" >&2; } +ok() { printf '%s\n' " ${GREEN}+${RESET} $*" >&2; } +warn() { printf '%s\n' " ${YELLOW}!${RESET} $*" >&2; } +die() { printf '%s\n' " ${RED}x $*${RESET}" >&2; exit 1; } +header(){ printf '\n%s\n\n' "${BOLD}${CYAN}--- $* ---${RESET}" >&2; } +have() { command -v "$1" >/dev/null 2>&1; } + +trap 'printf "%s\n" "${RED}x install failed${RESET}" >&2' ERR + +banner() { + printf '%s' "${CYAN}${BOLD}" >&2 + cat >&2 <<'ART' + + ___ _ __ ___ + / __| '__/ _ \ + | (__| | | __/ + \___|_| \___| +ART + printf '%s\n' "${RESET}" >&2 + printf '%s\n' " ${DIM}Credential Rotation Enforcer — policy enforcement across vaults and secret managers${RESET}" >&2 +} + +# ============================================================================ +# Privilege + package-manager fan +# ============================================================================ +SUDO="" +if [ "$(id -u)" -ne 0 ]; then + if have sudo; then SUDO="sudo"; fi +fi + +pkg_install() { + if have apt-get; then $SUDO apt-get update -y >/dev/null 2>&1 || warn "apt update had errors (often unrelated repos); continuing" + $SUDO apt-get install -y --no-install-recommends "$@" + elif have dnf; then $SUDO dnf install -y "$@" + elif have pacman; then $SUDO pacman -S --needed --noconfirm "$@" + elif have zypper; then $SUDO zypper install -y "$@" + elif have apk; then $SUDO apk add "$@" + elif have brew; then brew install "$@" + else return 1; fi +} + +# ============================================================================ +# Args +# ============================================================================ +usage() { + cat >&2 </dev/null || warn "pull failed; using existing clone" + else + info "cloning ${REPO_NAME}" + git clone --depth 1 --branch "$DEFAULT_BRANCH" --filter=blob:none --quiet "$REPO_URL" "$cache" \ + || die "clone failed from ${REPO_URL}" + fi + printf '%s\n' "$cache/$SUBDIR" +} -# 3) Shards -bold "Resolving shard dependencies..." -shards install -green "shards installed" +# ============================================================================ +# Crystal toolchain +# ============================================================================ +install_crystal() { + info "installing Crystal" + if have apt-get || have dnf || have zypper; then + if have curl; then curl -fsSL https://crystal-lang.org/install.sh | $SUDO bash + elif have wget; then wget -qO- https://crystal-lang.org/install.sh | $SUDO bash + else die "need curl or wget to install Crystal (or see https://crystal-lang.org/install/)"; fi + elif have pacman; then pkg_install crystal shards + elif have brew; then pkg_install crystal + else die "could not auto-install Crystal; see https://crystal-lang.org/install/"; fi +} -# 4) Build -bold "Building cre (release mode)..." -shards build cre --release -green "cre binary built at bin/cre ($(stat -c %s bin/cre 2>/dev/null || stat -f %z bin/cre) bytes)" +ensure_crystal() { + if have crystal; then + local ver; ver="$(crystal version 2>/dev/null | head -1 | awk '{print $2}')" + if [ -n "$ver" ] && [ "$(printf '%s\n%s\n' "$MIN_CRYSTAL" "$ver" | sort -V | head -1)" != "$MIN_CRYSTAL" ]; then + warn "Crystal $ver is older than $MIN_CRYSTAL — upgrading" + install_crystal + else + ok "Crystal ${ver:-detected}" + fi + else + install_crystal + fi + have crystal || die "Crystal still not on PATH after install; open a new shell and re-run" + have shards || die "shards (Crystal's dependency manager) not found after install" +} -# 5) Optional install to PATH -if [ "${INSTALL_TO_PATH:-0}" = "1" ]; then - bold "Installing to ${INSTALL_PREFIX}/bin/cre" - if [ -w "${INSTALL_PREFIX}/bin" ]; then - cp bin/cre "${INSTALL_PREFIX}/bin/cre" - else - sudo cp bin/cre "${INSTALL_PREFIX}/bin/cre" - fi - green "Installed: $(which cre)" +# ============================================================================ +# System build libraries (sqlite3 + Crystal's compile/runtime libs) +# ============================================================================ +ensure_build_deps() { + if [ -e /usr/include/sqlite3.h ] || [ -e /opt/homebrew/include/sqlite3.h ] || [ -e /usr/local/include/sqlite3.h ]; then + return + fi + info "installing build libraries (sqlite3 + ssl/pcre2/gmp/event)" + if have apt-get; then pkg_install libsqlite3-dev libssl-dev libpcre2-dev libgmp-dev libevent-dev zlib1g-dev libyaml-dev pkg-config build-essential + elif have dnf; then pkg_install sqlite-devel openssl-devel pcre2-devel gmp-devel libevent-devel zlib-devel libyaml-devel gcc make + elif have pacman; then pkg_install sqlite openssl pcre2 gmp libevent libyaml pkgconf + elif have zypper; then pkg_install sqlite3-devel libopenssl-devel pcre2-devel gmp-devel libevent-devel zlib-devel libyaml-devel gcc make + elif have apk; then pkg_install sqlite-dev openssl-dev pcre2-dev gmp-dev libevent-dev zlib-dev yaml-dev build-base + elif have brew; then pkg_install sqlite + else warn "unknown package manager — ensure sqlite3 + Crystal build libraries are installed"; fi +} + +# ============================================================================ +# Build + install onto PATH +# ============================================================================ +build_and_install() { + header "Building cre (release)" + info "resolving shard dependencies" + shards install >&2 + info "compiling the release binary" + shards build cre --release --no-debug >&2 + [ -x bin/cre ] || die "build produced no bin/cre" + strip -s bin/cre 2>/dev/null || true + mkdir -p "$INSTALL_DIR" + install -m 0755 bin/cre "$INSTALL_DIR/cre" + ok "installed cre -> ${INSTALL_DIR}/cre ($(du -h "$INSTALL_DIR/cre" | cut -f1))" +} + +wire_path() { + case ":$PATH:" in *":$INSTALL_DIR:"*) ok "$INSTALL_DIR already on PATH"; return ;; esac + local shell rc="" + shell="$(basename "${SHELL:-bash}")" + case "$shell" in + zsh) rc="$HOME/.zshrc" ;; + fish) mkdir -p "$HOME/.config/fish/conf.d" + echo "fish_add_path $INSTALL_DIR" > "$HOME/.config/fish/conf.d/cre.fish" + ok "added $INSTALL_DIR to PATH (fish)" ;; + bash) rc="$HOME/.bashrc"; [ -f "$rc" ] || rc="$HOME/.bash_profile" ;; + *) rc="$HOME/.profile" ;; + esac + if [ -n "$rc" ] && ! grep -q "$INSTALL_DIR" "$rc" 2>/dev/null; then + printf '\nexport PATH="%s:$PATH"\n' "$INSTALL_DIR" >> "$rc" + ok "added $INSTALL_DIR to PATH in $rc" + fi + export PATH="$INSTALL_DIR:$PATH" +} + +# ============================================================================ +# Main +# ============================================================================ +banner +have "$BINARY" && info "existing install at $(command -v "$BINARY") — updating" + +PROJECT="$(resolve_project)" +cd "$PROJECT" +ensure_crystal +ensure_build_deps +build_and_install +wire_path + +header "Verify" +if have "$BINARY"; then + ok "$($BINARY version 2>/dev/null || echo "$BINARY installed")" +else + warn "installed to $INSTALL_DIR but not yet on this shell's PATH" + warn "open a new terminal, or: export PATH=\"$INSTALL_DIR:\$PATH\"" fi -bold "" -bold "Try the zero-deps demo:" -echo " ./bin/cre demo" -bold "" -bold "Or start the daemon:" -echo " ./bin/cre run --db=sqlite:cre.db" -bold "" -bold "See learn/00-OVERVIEW.md for the full walkthrough." +printf '\n%s\n\n' " ${GREEN}${BOLD}cre is ready.${RESET}" >&2 +cat >&2 <