#!/usr/bin/env bash set -euo pipefail # Day-0 Git cutover for NetGescon. # Creates a clean project copy, initializes a fresh Git history, and can # optionally create/push to a Gitea repository via API. SOURCE_DIR="$(pwd)" TARGET_DIR="" BRANCH="main" COMMIT_MESSAGE="chore: NetGescon day-0 baseline" PUSH=0 FORCE=0 REPO_URL="" GITEA_URL="" GITEA_TOKEN="" GITEA_OWNER="" GITEA_REPO="" GITEA_OWNER_TYPE="user" # user|org GITEA_PRIVATE="true" usage() { cat <<'EOF' Usage: bash scripts/ops/netgescon-day0-cutover.sh [options] Options: --source Source project directory (default: current dir) --target Target clean directory (default: sibling day0 folder) --branch Git branch name (default: main) --message Initial commit message --force Remove existing target directory if present --push Push to remote after commit --repo-url Remote URL to set as origin --gitea-url Gitea base URL (e.g. https://git.netgescon.it) --gitea-token Gitea API token (admin or owner with repo rights) --gitea-owner Owner username or org name --gitea-repo Repository name to create --gitea-owner-type user|org (default: user) --gitea-private true|false (default: true) Examples: bash scripts/ops/netgescon-day0-cutover.sh --target /home/michele/netgescon/netgescon-day0 bash scripts/ops/netgescon-day0-cutover.sh \ --target /home/michele/netgescon/netgescon-day0 \ --push \ --gitea-url https://git.netgescon.it \ --gitea-token '***' \ --gitea-owner NetGescon \ --gitea-owner-type org \ --gitea-repo netgescon-day0 EOF } log() { printf "[day0] %s\n" "$*" } die() { printf "[day0][ERROR] %s\n" "$*" >&2 exit 1 } require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing command: $1" } while [[ $# -gt 0 ]]; do case "$1" in --source) SOURCE_DIR="$2"; shift 2 ;; --target) TARGET_DIR="$2"; shift 2 ;; --branch) BRANCH="$2"; shift 2 ;; --message) COMMIT_MESSAGE="$2"; shift 2 ;; --force) FORCE=1; shift ;; --push) PUSH=1; shift ;; --repo-url) REPO_URL="$2"; shift 2 ;; --gitea-url) GITEA_URL="$2"; shift 2 ;; --gitea-token) GITEA_TOKEN="$2"; shift 2 ;; --gitea-owner) GITEA_OWNER="$2"; shift 2 ;; --gitea-repo) GITEA_REPO="$2"; shift 2 ;; --gitea-owner-type) GITEA_OWNER_TYPE="$2"; shift 2 ;; --gitea-private) GITEA_PRIVATE="$2"; shift 2 ;; -h|--help) usage; exit 0 ;; *) die "Unknown option: $1" ;; esac done require_cmd rsync require_cmd git SOURCE_DIR="$(cd "$SOURCE_DIR" && pwd)" if [[ -z "$TARGET_DIR" ]]; then ts="$(date +%Y%m%d-%H%M%S)" TARGET_DIR="$(dirname "$SOURCE_DIR")/netgescon-day0-${ts}" fi if [[ ! -d "$SOURCE_DIR" ]]; then die "Source directory not found: $SOURCE_DIR" fi if [[ -e "$TARGET_DIR" ]]; then if [[ "$FORCE" -eq 1 ]]; then log "Removing existing target: $TARGET_DIR" rm -rf "$TARGET_DIR" else die "Target already exists: $TARGET_DIR (use --force)" fi fi log "Creating clean project copy" mkdir -p "$TARGET_DIR" rsync -a \ --exclude='.git/' \ --exclude='vendor/' \ --exclude='node_modules/' \ --exclude='storage/logs/' \ --exclude='storage/framework/cache/' \ --exclude='storage/framework/sessions/' \ --exclude='storage/framework/views/' \ --exclude='storage/backups/cutover-snapshots/' \ --exclude='.env' \ --exclude='.env.local' \ --exclude='.env.production' \ --exclude='.env.staging' \ --exclude='.env.development' \ --exclude='.env.test' \ --exclude='.env.testing' \ --exclude='*.log' \ --exclude='*.tmp' \ "$SOURCE_DIR"/ "$TARGET_DIR"/ cd "$TARGET_DIR" if [[ -f .env.example && ! -f .env ]]; then log "Keeping only .env.example in day-0 copy" fi # Add strict ignore guards for day-0 repository safety. cat >> .gitignore <<'EOF' # Day-0 safety guards .env .env.local .env.production .env.staging .env.development .env.test .env.testing vendor/ node_modules/ storage/logs/ storage/framework/cache/ storage/framework/sessions/ storage/framework/views/ storage/backups/ EOF log "Initializing fresh git repository" git init -q git checkout -b "$BRANCH" >/dev/null 2>&1 || git checkout "$BRANCH" name="$(git config --global --get user.name || true)" email="$(git config --global --get user.email || true)" if [[ -n "$name" ]]; then git config user.name "$name"; fi if [[ -n "$email" ]]; then git config user.email "$email"; fi git add -A if git diff --cached --quiet; then die "No files staged for initial commit. Check excludes." fi git commit -m "$COMMIT_MESSAGE" >/dev/null if [[ -n "$GITEA_URL" || -n "$GITEA_TOKEN" || -n "$GITEA_OWNER" || -n "$GITEA_REPO" ]]; then require_cmd curl require_cmd jq [[ -n "$GITEA_URL" ]] || die "--gitea-url is required when using Gitea API options" [[ -n "$GITEA_TOKEN" ]] || die "--gitea-token is required when using Gitea API options" [[ -n "$GITEA_OWNER" ]] || die "--gitea-owner is required when using Gitea API options" [[ -n "$GITEA_REPO" ]] || die "--gitea-repo is required when using Gitea API options" api_base="${GITEA_URL%/}/api/v1" payload="$(jq -n --arg name "$GITEA_REPO" --argjson private "$GITEA_PRIVATE" '{name:$name, private:$private, auto_init:false}')" if [[ "$GITEA_OWNER_TYPE" == "org" ]]; then create_url="$api_base/orgs/$GITEA_OWNER/repos" else create_url="$api_base/user/repos" fi log "Creating repository on Gitea via API" set +e create_resp="$(curl -sS -X POST "$create_url" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ -d "$payload")" create_code=$? set -e if [[ $create_code -ne 0 ]]; then die "Failed to call Gitea API" fi if echo "$create_resp" | jq -e '.id' >/dev/null 2>&1; then log "Repository created: $GITEA_OWNER/$GITEA_REPO" else # Repository may already exist; continue if URL can be inferred. message="$(echo "$create_resp" | jq -r '.message // empty')" log "Gitea API response: ${message:-non-standard response}" fi if [[ -z "$REPO_URL" ]]; then REPO_URL="ssh://git@${GITEA_URL#*://}:2222/${GITEA_OWNER}/${GITEA_REPO}.git" fi fi if [[ -n "$REPO_URL" ]]; then git remote add origin "$REPO_URL" log "Remote origin configured: $REPO_URL" fi if [[ "$PUSH" -eq 1 ]]; then [[ -n "$REPO_URL" ]] || die "Cannot push without remote. Use --repo-url or Gitea API options." log "Pushing day-0 baseline to origin/$BRANCH" git push -u origin "$BRANCH" fi log "Done" log "Clean repository path: $TARGET_DIR" log "Last commit: $(git rev-parse --short HEAD)"