tools: add repo creation script and helper README
Some checks failed
railiance-tests / smoke (push) Has been cancelled

This commit is contained in:
2025-09-12 02:11:37 +02:00
parent 6b9307289b
commit 59863b4214
3 changed files with 291 additions and 0 deletions

21
tools/README.md Normal file
View File

@@ -0,0 +1,21 @@
# Railiance Tools
## create_railiance_repo.sh
Creates a new Railiance repo in Gitea (user or org), with a minimal scaffold.
**Requires:** `~/.railiance_gitea.conf`
```bash
GITEA_URL="https://git.example.com"
GITEA_USER="your-username"
GITEA_ORG="" # or your org name
GITEA_TOKEN="xxxxxxxxxxxxxxxx" # scopes: read:user + write:repository
## Usage
./tools/create_railiance_repo.sh
./tools/create_railiance_repo.sh --repo my-repo --public --desc "My desc"
./tools/create_railiance_repo.sh --org coulomb
## Misc
The script relies on your Git credential helper or SSH key for the initial push.

View File

@@ -0,0 +1,269 @@
#!/usr/bin/env bash
# tools/create_railiance_repo.sh
#
# Railiance — sanitized bootstrap script for creating a Gitea repository.
#
# PURPOSE:
# Creates a repo (default: railiance-bootstrap) in your Gitea (user or org),
# scaffolds a minimal layout, commits, and pushes the initial content.
#
# SAFETY NOTES:
# - This script NEVER writes your credentials file and NEVER prints your token.
# - It requires ~/.railiance_gitea.conf to exist (see example below).
# - It relies on your local Git credential helper or SSH for the initial push.
# (Recommended: `git config --global credential.helper cache` or `store`)
#
# REQUIRED CONFIG (~/.railiance_gitea.conf):
# GITEA_URL="https://git.example.com" # no trailing slash; include subpath if any
# GITEA_USER="your-username" # your Gitea login (for user-owned repos)
# GITEA_ORG="" # optional; set to org name to create under org
# GITEA_TOKEN="xxxxxxxxxxxxxxxx" # personal access token with read:user + write:repository
#
# USAGE:
# ./tools/create_railiance_repo.sh
# ./tools/create_railiance_repo.sh --repo my-repo --desc "My desc" --public
# ./tools/create_railiance_repo.sh --org coulomb # override org from config
#
# FLAGS:
# --repo <name> : repository name (default: railiance-bootstrap)
# --desc <text> : description (default provided)
# --org <orgname> : create under this organization (overrides GITEA_ORG)
# --public : create as public repo (default: private)
# --no-scaffold : skip creating local scaffold (useful if repo already has content)
# -h|--help : show help
#
# EXITS non-zero on failure.
set -euo pipefail
# ----------------------------- parse args ------------------------------------
REPO_NAME="railiance-bootstrap"
REPO_DESC="Bootstrap repo for Railiance IaC. Recreates Coulomb infra from scratch."
IS_PRIVATE=true
OVERRIDE_ORG=""
DO_SCAFFOLD=true
while [[ $# -gt 0 ]]; do
case "$1" in
--repo) REPO_NAME="${2:?}"; shift 2 ;;
--desc) REPO_DESC="${2:?}"; shift 2 ;;
--org) OVERRIDE_ORG="${2:?}"; shift 2 ;;
--public) IS_PRIVATE=false; shift ;;
--no-scaffold)DO_SCAFFOLD=false; 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
# ----------------------------- load config -----------------------------------
CONFIG_FILE="${HOME}/.railiance_gitea.conf"
if [[ ! -f "${CONFIG_FILE}" ]]; then
echo "ERROR: Missing config ${CONFIG_FILE}"
echo "Create it with:"
cat <<'EOT'
GITEA_URL="https://git.example.com"
GITEA_USER="your-username"
GITEA_ORG="" # or set to your org
GITEA_TOKEN="xxxxxxxxxxxxxxxx"
EOT
exit 1
fi
# shellcheck disable=SC1090
source "${CONFIG_FILE}"
: "${GITEA_URL:?Set GITEA_URL in ${CONFIG_FILE}}"
: "${GITEA_USER:?Set GITEA_USER in ${CONFIG_FILE}}"
: "${GITEA_TOKEN:?Set GITEA_TOKEN in ${CONFIG_FILE}}"
: "${GITEA_ORG:=}"
if [[ -n "${OVERRIDE_ORG}" ]]; then
GITEA_ORG="${OVERRIDE_ORG}"
fi
# ----------------------------- helpers ---------------------------------------
need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "Missing: $1" >&2; exit 1; }; }
need_cmd curl
need_cmd git
json_escape() {
# minimal escape for description text
printf '%s' "$1" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
}
curl_code() {
# prints only HTTP code; follows redirects; uses token header
curl -sS -o /dev/null -w "%{http_code}" -L \
-H "Authorization: token ${GITEA_TOKEN}" "$@"
}
repo_exists() {
local owner="$1" repo="$2" code
code=$(curl_code "${GITEA_URL}/api/v1/repos/${owner}/${repo}")
[[ "${code}" == "200" ]]
}
create_repo_user() {
local repo="$1" private="$2" desc_json="$3"
curl_code -X POST "${GITEA_URL}/api/v1/user/repos" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${repo}\",\"private\":${private},\"description\":${desc_json}}"
}
create_repo_org() {
local org="$1" repo="$2" private="$3" desc_json="$4"
curl_code -X POST "${GITEA_URL}/api/v1/orgs/${org}/repos" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${repo}\",\"private\":${private},\"description\":${desc_json}}"
}
scaffold_repo() {
local dir="$1"
mkdir -p "${dir}"/{ansible,helm,k8s,tests,docs}
cat > "${dir}/README.md" <<EOF
# ${REPO_NAME}
**Railiance Bootstrap** — opinionated, reproducible IaC to rebuild Coulomb infra from scratch.
## Goals
- Two Linux machines + this Git repo + credentials ⇒ full rebuild
- GitOps-first with ArgoCD/Flux
- OODA (Observe→Orient→Decide→Act) encoded as pipelines
- Tests define success at every step
## Layout
\`\`\`
ansible/ # host bootstrap
helm/ # charts & values
k8s/ # raw manifests/CRDs
tests/ # expectation tests (bash/py)
docs/ # operator notes, OODA SOPs
\`\`\`
EOF
cat > "${dir}/.gitignore" <<'EOF'
# OS
.DS_Store
# Python
__pycache__/
*.pyc
# Ansible artifacts
*.retry
# Helm build
charts/*/charts/
charts/*/tmp/
# Local secrets (do not commit)
secrets/
*.enc
EOF
cat > "${dir}/ansible/bootstrap.yml" <<'EOF'
---
- name: Railiance host bootstrap (example)
hosts: all
become: true
tasks:
- name: Ensure base packages
apt:
name:
- curl
- git
- jq
update_cache: yes
state: present
EOF
cat > "${dir}/tests/smoke_kube.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "[placeholder] Add kube API checks here (kubectl get nodes/ns)"
EOF
chmod +x "${dir}/tests/smoke_kube.sh"
cat > "${dir}/docs/OODA.md" <<'EOF'
# OODA in Railiance
- Observe: telemetry & test results
- Orient: AI analyzes deltas & proposes remediations
- Decide: specify → review → authorize (Git PR)
- Act: GitOps applies automatically (human independent)
EOF
}
git_init_and_push() {
local dir="$1" remote_url="$2"
(
cd "${dir}"
git init
git add .
git commit -m "railiance: initial bootstrap scaffold"
git branch -M main
git remote add origin "${remote_url}"
# relies on credential helper or SSH setup; avoids embedding token in URL
git push -u origin main
)
}
# ----------------------------- main ------------------------------------------
DESC_JSON=$(json_escape "${REPO_DESC}")
PRIVATE_JSON=$([[ "${IS_PRIVATE}" == true ]] && echo true || echo false)
OWNER="${GITEA_USER}"
REMOTE_URL="${GITEA_URL}/${OWNER}/${REPO_NAME}.git"
if [[ -n "${GITEA_ORG}" ]]; then
OWNER="${GITEA_ORG}"
REMOTE_URL="${GITEA_URL}/${OWNER}/${REPO_NAME}.git"
fi
# Basic API reachability check (no auth needed)
VER_CODE=$(curl_code "${GITEA_URL}/api/v1/version")
if [[ "${VER_CODE}" != "200" ]]; then
echo "ERROR: Gitea API not reachable at ${GITEA_URL}/api/v1/version (HTTP ${VER_CODE})" >&2
exit 1
fi
if repo_exists "${OWNER}" "${REPO_NAME}"; then
echo "Repo ${OWNER}/${REPO_NAME} already exists. Skipping creation."
else
if [[ -n "${GITEA_ORG}" ]]; then
echo "Creating repo under org '${GITEA_ORG}'..."
CODE=$(create_repo_org "${GITEA_ORG}" "${REPO_NAME}" "${PRIVATE_JSON}" "${DESC_JSON}")
if [[ "${CODE}" != "201" ]]; then
echo "ERROR: Failed to create repo in org (HTTP ${CODE}). Do you have org permissions?" >&2
exit 1
fi
else
echo "Creating repo under user '${GITEA_USER}'..."
CODE=$(create_repo_user "${REPO_NAME}" "${PRIVATE_JSON}" "${DESC_JSON}")
if [[ "${CODE}" != "201" ]]; then
echo "ERROR: Failed to create user repo (HTTP ${CODE}). Check token scopes." >&2
exit 1
fi
fi
echo "Repo created: ${OWNER}/${REPO_NAME}"
fi
if [[ "${DO_SCAFFOLD}" == true ]]; then
if [[ -d "${REPO_NAME}" ]]; then
echo "Local dir '${REPO_NAME}' exists. Skipping scaffold."
else
echo "Scaffolding '${REPO_NAME}'..."
mkdir -p "${REPO_NAME}"
scaffold_repo "${REPO_NAME}"
fi
echo "Pushing initial commit to ${REMOTE_URL} ..."
git_init_and_push "${REPO_NAME}" "${REMOTE_URL}"
else
echo "Skipping scaffold as requested (--no-scaffold)."
fi
echo "✅ Done."
echo "Repository: ${OWNER}/${REPO_NAME}"
echo "Remote URL: ${REMOTE_URL}"