#!/usr/bin/env bash # tools/create_railiance_overlay_repo.sh # Create a local Railiance overlay repo skeleton for a third-party upstream app. set -euo pipefail APP_ID="" APP_NAME="" OWNER="platform" CRITICALITY="medium" UPSTREAM_URL="" UPSTREAM_REVISION="main" UPSTREAM_TRACKING="branch" OUT_DIR="" INIT_GIT=false usage() { cat <<'EOF' Usage: tools/create_railiance_overlay_repo.sh --app-id --upstream-url [options] Required: --app-id Stable lowercase app id, e.g. forgejo --upstream-url Upstream source repository or release URL Options: --name Human-readable app name (default: app id) --owner Owning team/domain (default: platform) --criticality low|medium|high|critical (default: medium) --upstream-revision Upstream branch/tag/commit/release (default: main) --upstream-tracking branch|tag|commit|release|digest (default: branch) --out-dir Output directory (default: -railiance-overlay) --init-git Initialize a local Git repo, without committing -h|--help Show this help The script writes local files only. It does not clone upstream code, call Gitea, fetch secrets, or push a remote. EOF } while [[ $# -gt 0 ]]; do case "$1" in --app-id) APP_ID="${2:?}"; shift 2 ;; --name) APP_NAME="${2:?}"; shift 2 ;; --owner) OWNER="${2:?}"; shift 2 ;; --criticality) CRITICALITY="${2:?}"; shift 2 ;; --upstream-url) UPSTREAM_URL="${2:?}"; shift 2 ;; --upstream-revision) UPSTREAM_REVISION="${2:?}"; shift 2 ;; --upstream-tracking) UPSTREAM_TRACKING="${2:?}"; shift 2 ;; --out-dir) OUT_DIR="${2:?}"; shift 2 ;; --init-git) INIT_GIT=true; shift ;; -h|--help) usage; exit 0 ;; *) echo "Unknown arg: $1" >&2; usage >&2; exit 2 ;; esac done if [[ -z "${APP_ID}" || -z "${UPSTREAM_URL}" ]]; then echo "ERROR: --app-id and --upstream-url are required" >&2 usage >&2 exit 2 fi if [[ ! "${APP_ID}" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then echo "ERROR: --app-id must match ^[a-z0-9][a-z0-9-]*$" >&2 exit 2 fi case "${CRITICALITY}" in low|medium|high|critical) ;; *) echo "ERROR: --criticality must be low, medium, high, or critical" >&2; exit 2 ;; esac case "${UPSTREAM_TRACKING}" in branch|tag|commit|release|digest) ;; *) echo "ERROR: --upstream-tracking must be branch, tag, commit, release, or digest" >&2; exit 2 ;; esac if [[ -z "${APP_NAME}" ]]; then APP_NAME="${APP_ID}" fi if [[ -z "${OUT_DIR}" ]]; then OUT_DIR="${APP_ID}-railiance-overlay" fi if [[ -e "${OUT_DIR}" ]]; then if [[ -n "$(ls -A "${OUT_DIR}")" ]]; then echo "ERROR: output directory exists and is not empty: ${OUT_DIR}" >&2 exit 1 fi fi mkdir -p \ "${OUT_DIR}/railiance" \ "${OUT_DIR}/charts/${APP_ID}/templates" \ "${OUT_DIR}/values" \ "${OUT_DIR}/patches/upstream" \ "${OUT_DIR}/tests" \ "${OUT_DIR}/runbooks" \ "${OUT_DIR}/docs" touch "${OUT_DIR}/patches/upstream/.gitkeep" cat > "${OUT_DIR}/README.md" < "${OUT_DIR}/railiance/upstream.toml" < "${OUT_DIR}/railiance/app.toml" < "${OUT_DIR}/charts/${APP_ID}/Chart.yaml" < "${OUT_DIR}/charts/${APP_ID}/values.yaml" < "${OUT_DIR}/charts/${APP_ID}/templates/deployment.yaml" <<'EOF' apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Release.Name }} labels: app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: labels: app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: {{ .Values.service.port }} resources: {{ toYaml .Values.resources | indent 12 }} EOF cat > "${OUT_DIR}/charts/${APP_ID}/templates/service.yaml" <<'EOF' apiVersion: v1 kind: Service metadata: name: {{ .Release.Name }} labels: app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} spec: selector: app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} ports: - name: http port: {{ .Values.service.port }} targetPort: http EOF cat > "${OUT_DIR}/values/stage1.yaml" < "${OUT_DIR}/values/stage2-canary.yaml" < "${OUT_DIR}/values/stage3-production.yaml" < "${OUT_DIR}/tests/stage1.sh" </dev/null 2>&1; then helm template ${APP_ID}-local charts/${APP_ID} -f values/stage1.yaml >/tmp/${APP_ID}-stage1-render.yaml echo 'helm template ok' else echo 'helm unavailable; skipped helm template check' fi EOF chmod +x "${OUT_DIR}/tests/stage1.sh" cat > "${OUT_DIR}/runbooks/rollback.md" < "${OUT_DIR}/docs/promotion.md" < "${OUT_DIR}/.gitignore" <<'EOF' .DS_Store __pycache__/ *.pyc *.log *.tmp *.bak .secrets/ secrets/ *.kubeconfig .railiance_gitea.conf EOF if [[ "${INIT_GIT}" == true ]]; then git -C "${OUT_DIR}" init fi echo "Created Railiance overlay repo skeleton: ${OUT_DIR}" echo "Next: edit railiance/app.toml, run tests/stage1.sh, then commit the overlay repo."