#!/usr/bin/env bash set -euo pipefail if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "[pousse][ERROR] Not in a git repository" >&2 exit 1 fi PROJECT_ROOT="$(git rev-parse --show-toplevel)" SCRIPT_REAL="$(readlink -f "${BASH_SOURCE[0]:-$0}" 2>/dev/null || realpath "${BASH_SOURCE[0]:-$0}" 2>/dev/null || echo "${BASH_SOURCE[0]:-$0}")" DEPLOY_DIR="$(cd "$(dirname "$SCRIPT_REAL")" && pwd)" IA_DEV_ROOT="$(cd "$DEPLOY_DIR/.." && pwd)" # Optional project id: --project or first positional if projects//conf.json exists while [[ $# -gt 0 ]]; do if [[ "$1" == --project ]]; then [[ -n "${2:-}" ]] || { echo "[pousse][ERROR] --project requires " >&2; exit 1; } export IA_PROJECT_ID="$2" shift 2 elif [[ "$1" != --* && -n "${1:-}" && -f "${IA_DEV_ROOT}/projects/${1}/conf.json" ]]; then export IA_PROJECT_ID="$1" shift else break fi done # Resolve project id and config path: param (IA_PROJECT_ID), MAIL_TO or AI_AGENT_TOKEN → projects//conf.json # shellcheck source=../lib/project_config.sh source "${IA_DEV_ROOT}/lib/project_config.sh" [[ -n "${PROJECT_ID:-}" ]] && export IA_PROJECT_ID="$PROJECT_ID" remote="origin" bump_version=false usage() { cat <<'EOF' Usage: ./deploy/pousse.sh [project_id|--project ] [--remote ] [--bump-version] project_id Optional. Id from projects//conf.json (e.g. lecoffreio). Else from MAIL_TO or AI_AGENT_TOKEN. --project Same as positional project_id. --bump-version Increment patch (third component) in VERSION before staging. Reads a full multi-line commit message from STDIN, then: - if not in repo root: re-exec from repo root (standardized execution) - build check (npm run build in each directory listed in projects//conf.json build_dirs, if any; exit on failure) - git add -A - git commit -F - git push -u HEAD The current branch must already exist on the remote (e.g. origin/); otherwise the script refuses to push. Example: ./deploy/pousse.sh <<'MSG' Title **Motivations:** - ... **Root causes:** - ... **Correctifs:** - ... **Evolutions:** - ... **Pages affectées:** - ... MSG EOF } while [[ $# -gt 0 ]]; do case "$1" in --remote) remote="$2" shift 2 ;; --bump-version) bump_version=true shift ;; -h|--help) usage exit 0 ;; *) echo "[pousse][ERROR] Unknown arg: $1" >&2 usage >&2 exit 1 ;; esac done repo_root="$(git rev-parse --show-toplevel)" # When run from ia_dev root, use configured project repo for git operations (MAIL_TO or AI_AGENT_TOKEN must be set) git_work_root="$repo_root" if [[ -n "${PROJECT_CONFIG_PATH:-}" && -f "$PROJECT_CONFIG_PATH" ]] && command -v jq >/dev/null 2>&1 && [[ "$repo_root" == "$IA_DEV_ROOT" ]]; then _secrets_path="$(jq -r '.deploy.secrets_path // ""' "$PROJECT_CONFIG_PATH" 2>/dev/null)" if [[ -n "$_secrets_path" ]]; then _gwr="$(dirname "$_secrets_path")" if [[ -d "$_gwr" ]]; then git_work_root="$_gwr" fi fi fi if [[ "$(pwd)" != "$git_work_root" ]]; then cd "$git_work_root" || { echo "[pousse][ERROR] Cannot cd to project root ${git_work_root}" >&2; exit 1; } fi branch="$(git rev-parse --abbrev-ref HEAD)" if [[ -z "$branch" || "$branch" == "HEAD" ]]; then echo "[pousse][ERROR] Detached HEAD is not supported" >&2 exit 1 fi author_name="$(git config user.name || true)" if [[ "$author_name" != "4NK" && "$author_name" != "Nicolas Cantu" ]]; then echo "[pousse][ERROR] Refusing to commit: git user.name must be '4NK' or 'Nicolas Cantu' (got: '${author_name}')" >&2 exit 1 fi # Build dirs from project config (projects//conf.json); skip if no config or no build_dirs build_dirs=() if [[ -n "${PROJECT_CONFIG_PATH:-}" && -f "$PROJECT_CONFIG_PATH" ]] && command -v jq >/dev/null 2>&1; then while IFS= read -r d; do [[ -n "$d" ]] && build_dirs+=( "$d" ) done < <(jq -r '.build_dirs[]? // empty' "$PROJECT_CONFIG_PATH" 2>/dev/null) fi if [[ ${#build_dirs[@]} -gt 0 ]]; then echo "[pousse] Build check (${#build_dirs[@]} dirs from project config)..." for dir in "${build_dirs[@]}"; do if [[ "$dir" = /* ]]; then abs_dir="$dir" else abs_dir="${repo_root}/${dir}" fi if [[ ! -d "$abs_dir" ]]; then echo "[pousse][WARN] Skipping build ${dir} (directory not found)" >&2 continue fi echo "[pousse] Building ${dir}..." (cd "$abs_dir" && npm run build) || { echo "[pousse][ERROR] Build failed in ${dir}" >&2 exit 1 } done echo "[pousse] Build check OK" else echo "[pousse] No build_dirs in project config (or no projects//conf.json / jq); skipping build check" fi msg_file="$(mktemp -t pousse-commit-msg.XXXXXX)" cleanup() { rm -f "$msg_file" } trap cleanup EXIT cat >"$msg_file" || true if [[ ! -s "$msg_file" ]]; then echo "[pousse][ERROR] Empty commit message on STDIN" >&2 exit 1 fi if [[ "$bump_version" == "true" ]]; then version_file="${git_work_root}/VERSION" if [[ ! -f "$version_file" ]]; then echo "[pousse][ERROR] VERSION not found at ${version_file}" >&2 exit 1 fi current="$(cat "$version_file" | sed 's/[[:space:]]//g')" if ! [[ "$current" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "[pousse][ERROR] VERSION format must be X.Y.Z (got: '${current}')" >&2 exit 1 fi maj="${current%%.*}" min="${current#*.}" min="${min%%.*}" patch="${current##*.}" patch=$((patch + 1)) new_version="${maj}.${min}.${patch}" echo "$new_version" > "$version_file" echo "[pousse] Bumped VERSION: ${current} -> ${new_version}" fi # Stage all changes git add -A git_status_short="$(git status -sb)" echo "$git_status_short" # Prevent committing potentially sensitive files staged_files="$(git diff --cached --name-only || true)" if [[ -n "$staged_files" ]]; then if echo "$staged_files" | grep -Eiq '^(\.secrets/|\.env($|\.)|\.env\.|.*\.(key|pem|p12)$|.*credentials.*)'; then echo "[pousse][ERROR] Refusing to commit: staged files look sensitive:" >&2 echo "$staged_files" | grep -Ei '^(\.secrets/|\.env($|\.)|\.env\.|.*\.(key|pem|p12)$|.*credentials.*)' >&2 exit 1 fi fi if git diff --cached --quiet; then echo "[pousse] No staged changes to commit" >&2 exit 0 fi echo "[pousse] Staged changes:" git diff --cached --stat git commit -F "$msg_file" if ! git rev-parse "${remote}/${branch}" >/dev/null 2>&1; then echo "[pousse][ERROR] Branch '${branch}' does not exist on remote '${remote}'. Refusing to push (would create a new remote branch). Create the branch on the remote first or push manually." >&2 exit 1 fi git push -u "$remote" HEAD