diff --git a/tools/README_tools.md b/tools/README_tools.md new file mode 100644 index 0000000..624c6ed --- /dev/null +++ b/tools/README_tools.md @@ -0,0 +1,62 @@ +# 🌱 Railiance Tools β€” Directed Panspermia Model + +Railiance bootstraps infrastructure in a way inspired by **directed panspermia** β€” +the idea of life being intentionally seeded across the cosmos. +Here, instead of biology, we seed **infrastructure-as-code** into blank environments. + +--- + +## 🧬 Core Concepts + +- **Parent Body** β€” an existing Railiance repo (the source of life). +- **Seed** β€” the minimal set of instructions/scripts to reproduce the repo in a new environment. +- **Spore** β€” a portable, bundled package of the Seed (self-contained, easy to transfer). +- **Target Environment** β€” a blank host (a sterile world waiting for life). +- **Biogenesis** β€” the process of unpacking a Spore and bootstrapping the environment. +- **Evolutionary Adaptation** β€” further provisioning, testing, and tuning specific to the host. + +--- + +## πŸ› οΈ Toolchain Overview + +### `create_railiance_repo.sh` +- Creates a new Railiance repo in Gitea (under user or org). +- Acts like the *egg cell*: it defines the first container for life. + +### `furnish_railiance_repo.sh` +- Idempotently sets up repo housekeeping files (LICENSE, README, .editorconfig, etc.). +- Brings the repo to a clean, opinionated state. +- Equivalent to providing the *genetic code*. + +### `build_spore.sh` +- Packages the Seed into a **Spore bundle** (tar.gz). +- Includes creation/furnishing scripts, docs, and metadata. +- Like enclosing life into a *protective capsule* for travel. + +### `seed_node.sh` +- Run on a blank host to unpack a Spore. +- Performs **Biogenesis**: installs prerequisites, clones the repo, and executes bootstrap steps. +- Like a *spore germinating* on a fertile planet. + +--- + +## πŸš€ Lifecycle + +1. **Create** a repo with `create_railiance_repo.sh`. +2. **Furnish** it into a clean Railiance structure with `furnish_railiance_repo.sh`. +3. **Build** a distributable bundle with `build_spore.sh`. +4. **Seed** new environments with `seed_node.sh`. + +--- + +## 🌍 Why Panspermia? + +This model emphasizes: +- **Reproducibility**: every host can be bootstrapped identically from a Spore. +- **Federation**: Spores can be sent to many environments (different orgs, clusters, or even air-gapped hosts). +- **Resilience**: infrastructure survives and adapts, no matter the environment. +- **Storytelling**: by using biological metaphors, we keep the process intuitive and memorable. + +--- + +✦ Railiance is not just code β€” it’s a way of letting infrastructure **colonize new worlds**. diff --git a/tools/build_spore.sh b/tools/build_spore.sh new file mode 100644 index 0000000..9a09669 --- /dev/null +++ b/tools/build_spore.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# tools/build_spore.sh +# Package a "Spore" bundle: minimal bootstrap seed + furnishing scripts + metadata + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +outdir="${repo_root}/dist" +mkdir -p "${outdir}" + +timestamp="$(date -u +%Y%m%dT%H%M%SZ)" +spore_name="railiance_spore_${timestamp}.tar.gz" +spore_path="${outdir}/${spore_name}" + +echo "[*] Building spore bundle β†’ ${spore_path}" + +# Metadata file +cat > "${outdir}/SPORE_METADATA.txt" </dev/null || echo "N/A") +META + +# Bundle essential files +tar -czf "${spore_path}" -C "${repo_root}" tools/create_railiance_repo.sh tools/furnish_railiance_repo.sh tools/build_spore.sh tools/seed_node.sh LICENSE README.md .editorconfig .gitattributes .gitignore docs/ -C "${outdir}" SPORE_METADATA.txt + +echo "[βœ“] Spore bundle created: ${spore_path}" diff --git a/tools/furnish_railiance_repo.sh b/tools/furnish_railiance_repo.sh new file mode 100644 index 0000000..2713c30 --- /dev/null +++ b/tools/furnish_railiance_repo.sh @@ -0,0 +1,199 @@ +#!/usr/bin/env bash +# tools/furnish_railiance_repo.sh +# Idempotently "furnishes" the repo with license, readmes, and housekeeping files. + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +ensure_file() { # ensure_file [mode] + local path="$1"; shift + local label="$1"; shift + local mode="${1:-}" + if [[ -f "${path}" ]]; then + echo "[=] exists: ${path}" + else + echo "[+] create: ${path}" + mkdir -p "$(dirname "${path}")" + eval "cat <<'${label}' > '${path}'" + if [[ -n "${mode}" ]]; then chmod "${mode}" "${path}"; fi + fi +} + +append_once() { # append_once + local path="$1" needle="$2" text="$3" + grep -qF "${needle}" "${path}" 2>/dev/null || { echo "${text}" >> "${path}"; echo "[+] append β†’ ${path}"; } +} + +# LICENSE (MIT) +ensure_file "${repo_root}/LICENSE" LIC <<'LIC' +MIT License + +Copyright (c) 2025 Railiance Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +LIC + +# Root README with badge (only if missing) +ensure_file "${repo_root}/README.md" RREAD <<'RREAD' +# Railiance Bootstrap +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +Railiance is an opinionated **Infrastructure-as-Code framework** β€” +think *Rails for Ops*: convention over configuration, reproducibility first. + +This repo (`railiance-bootstrap`) is the **entry point**: +from two bare Linux servers, a Git repo, and credentials, you can rebuild +a fully automated Kubernetes-based environment. +RREAD + +# docs/README +ensure_file "${repo_root}/docs/README.md" DREAD <<'DREAD' +# Railiance Documentation + +Welcome to **Railiance** β€” an opinionated framework for Infrastructure-as-Code. +Think of it as *Rails for Ops*: convention over configuration, productivity first. +DREAD + +# CONTRIBUTING +ensure_file "${repo_root}/docs/CONTRIBUTING.md" CONTRIB <<'CONTRIB' +# Contributing to Railiance +(…short rules: no secrets, LF endings, PRs w/ tests…) +CONTRIB + +# .editorconfig +ensure_file "${repo_root}/.editorconfig" ECONF <<'ECONF' +root = true +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +[*.sh] +indent_style = space +indent_size = 2 + +[*.py] +indent_style = space +indent_size = 4 +ECONF + +# .gitattributes +ensure_file "${repo_root}/.gitattributes" GATTR <<'GATTR' +* text=auto eol=lf +*.sh text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.md text eol=lf +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.svg binary +*.ico binary +*.pdf binary +*.zip binary +*.tar.gz binary +*.tgz binary +.gitattributes text eol=lf +.editorconfig text eol=lf +GATTR + +# .gitignore +ensure_file "${repo_root}/.gitignore" GIGN <<'GIGN' +.DS_Store +Thumbs.db +*.swp +*.swo +*.bak +*.tmp +*~ + +__pycache__/ +*.pyc +*.pyo +*.pyd +*.egg-info/ +.eggs/ +.venv/ +venv/ +env/ + +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log +.pnpm-debug.log + +*.retry +inventory/ +hosts +.secrets/ +.vault_pass.txt + +charts/*.tgz +.helm/ +kustomize-build/ +.kube/ +*.kubeconfig + +.terraform/ +terraform.tfstate +terraform.tfstate.backup +*.tfvars + +logs/ +*.log +.coverage +coverage.xml +htmlcov/ +.junit/ +*.out +*.err + +.gitattributes.lock +.editorconfig.lock + +.railiance_gitea.conf + +.vscode/ +.idea/ +*.iml +GIGN + +# tools/README +ensure_file "${repo_root}/tools/README.md" TREAD <<'TREAD' +# Railiance Tools +- `create_railiance_repo.sh` β€” create a Gitea repo (user/org). +- `furnish_railiance_repo.sh` β€” idempotently add license/housekeeping to this repo. +- `build_spore.sh` β€” produce a portable seed bundle (see panspermia model). +- `seed_node.sh` β€” run on a blank host to kick off biogenesis. +TREAD + +echo "[βœ“] Furnishing complete." diff --git a/tools/seed_node.sh b/tools/seed_node.sh new file mode 100644 index 0000000..7563311 --- /dev/null +++ b/tools/seed_node.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# tools/seed_node.sh +# Railiance Seed β€” kick off biogenesis on a blank (or nearly blank) host. +# +# Responsibilities: +# - Ensure minimal prerequisites (curl, git, jq) +# - Discover metadata (panspermia.json or env vars) +# - Clone or update the parent repo (default: railiance-bootstrap) +# - Run furnishing (idempotent) to align housekeeping +# - (Optional) handoff to further bootstrap steps +# +# Usage examples: +# ./tools/seed_node.sh +# REPO_URL=https://git.example.com/org/railiance-bootstrap.git ./tools/seed_node.sh +# ./tools/seed_node.sh --repo-dir /srv/railiance --branch main +# +# Notes: +# - No secrets are written or echoed by this script. +# - Git authentication relies on your environment (SSH keys or credential helper). + +set -euo pipefail + +# --------- defaults & flags ---------- +REPO_DIR="${REPO_DIR:-railiance-bootstrap}" +BRANCH="${BRANCH:-main}" +META_PATH="${META_PATH:-}" # optional explicit path to panspermia.json +QUIET=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --repo-dir) REPO_DIR="${2:?}"; shift 2 ;; + --branch) BRANCH="${2:?}"; shift 2 ;; + --meta) META_PATH="${2:?}"; shift 2 ;; + -q|--quiet) QUIET=true; shift ;; + -h|--help) + sed -n '1,120p' "$0" | sed -n '1,/^set -euo pipefail/p' + exit 0 ;; + *) echo "Unknown arg: $1" >&2; exit 2 ;; + esac +done + +log() { $QUIET || echo "$@"; } + +need() { + command -v "$1" >/dev/null 2>&1 && return 0 + log "[*] Installing $1 ..." + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update -y && sudo apt-get install -y "$1" + elif command -v dnf >/dev/null 2>&1; then + sudo dnf install -y "$1" + elif command -v yum >/dev/null 2>&1; then + sudo yum install -y "$1" + else + echo "ERROR: package manager not found to install $1" >&2 + exit 1 + fi +} + +# prerequisites +need curl +need git +command -v jq >/dev/null 2>&1 || need jq + +# --------- discover metadata ---------- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEFAULT_META="${SCRIPT_DIR}/../panspermia.json" + +REPO_URL="${REPO_URL:-}" +if [[ -z "${REPO_URL}" ]]; then + # read metadata file (explicit > default > none) + SRC_META="" + if [[ -n "${META_PATH}" && -f "${META_PATH}" ]]; then + SRC_META="${META_PATH}" + elif [[ -f "${DEFAULT_META}" ]]; then + SRC_META="${DEFAULT_META}" + fi + + if [[ -n "${SRC_META}" ]]; then + log "[=] Using metadata: ${SRC_META}" + REPO_URL="$(jq -r '.parent_body.repo_url // empty' "${SRC_META}")" + BRANCH_FROM_META="$(jq -r '.parent_body.branch // empty' "${SRC_META}")" || true + if [[ -n "${BRANCH_FROM_META}" ]]; then BRANCH="${BRANCH_FROM_META}"; fi + fi +fi + +if [[ -z "${REPO_URL}" ]]; then + echo "ERROR: No REPO_URL provided and no panspermia metadata found." >&2 + echo "Provide one of:" >&2 + echo " - env REPO_URL=https://git.example.com/org/railiance-bootstrap.git" >&2 + echo " - or a panspermia.json with .parent_body.repo_url" >&2 + exit 1 +fi + +log "[*] Target repo: ${REPO_URL} (branch: ${BRANCH})" +log "[*] Local dir : ${REPO_DIR}" + +# --------- clone or update ---------- +if [[ -d "${REPO_DIR}/.git" ]]; then + log "[=] Repo dir exists. Fetching updates ..." + ( + cd "${REPO_DIR}" + git remote -v + git fetch --all --tags + git checkout "${BRANCH}" + git pull --ff-only origin "${BRANCH}" || true + ) +else + log "[+] Cloning repository ..." + git clone --branch "${BRANCH}" "${REPO_URL}" "${REPO_DIR}" +fi + +# --------- furnishing ---------- +if [[ -x "${REPO_DIR}/tools/furnish_railiance_repo.sh" ]]; then + log "[*] Running furnishing script (idempotent) ..." + bash "${REPO_DIR}/tools/furnish_railiance_repo.sh" +else + log "[!] Furnishing script not found at ${REPO_DIR}/tools/furnish_railiance_repo.sh β€” skipping." +fi + +# --------- placeholder for next steps ---------- +cat <<'NEXT' + +[βœ“] Seed completed. + +Next steps (manual, for now): + 1) Review the repo at $REPO_DIR + 2) Configure host inventory and credentials (kept local, never committed) + 3) Run the initial bootstrap playbook (e.g., ansible/bootstrap.yml) + 4) Prepare GitOps operator (ArgoCD/Flux) pointing to this repo + +Hints: + - To use SSH instead of HTTPS, set REPO_URL=git@your-gitea:org/railiance-bootstrap.git + - If using HTTPS, set up 'git config --global credential.helper cache|store' + - For air-gapped: copy a Spore bundle, extract, then run this seed script + +NEXT