192 lines
5.2 KiB
Bash
Executable File
192 lines
5.2 KiB
Bash
Executable File
#!/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)"
|
|
if [[ "$(pwd)" != "$PROJECT_ROOT" ]]; then
|
|
SCRIPT_ABS="${DEPLOY_DIR}/$(basename "${BASH_SOURCE[0]:-$0}")"
|
|
cd "$PROJECT_ROOT" && exec "$SCRIPT_ABS" "$@"
|
|
fi
|
|
|
|
# Resolve project id and config path: MAIL_TO or AI_AGENT_TOKEN only (no fallback) → projects/<id>/conf.json
|
|
# shellcheck source=../lib/project_config.sh
|
|
source "${IA_DEV_ROOT}/lib/project_config.sh"
|
|
|
|
remote="origin"
|
|
bump_version=false
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
./deploy/pousse.sh [--remote <remote>] [--bump-version]
|
|
|
|
--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/<id>/conf.json build_dirs, if any; exit on failure)
|
|
- git add -A
|
|
- git commit -F <message>
|
|
- git push -u <remote> HEAD
|
|
|
|
The current branch must already exist on the remote (e.g. origin/<branch>); 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
|
|
|
|
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
|
|
|
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
# Build dirs from project config (projects/<id>/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/<id>/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="${repo_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
|