ia_dev/deploy/pousse.sh
Nicolas Cantu 07e0341c1d Initial commit: ia_dev pilot repo, agents and deploy scripts
**Motivations:**
- Provide a single repo for IA-driven piloting of all projects (agents, rules, deploy scripts).
- Reusable as git submodule; project-specific config in projects/ (no slug from submodule path).

**Evolutions:**
- Cursor agents: deploy-by-script, push-by-script, branch-align, fix, evol, fix-lint, fix-search, code, docupdate, gitea-issues-process, change-to-all-branches.
- Deploy scripts: pousse.sh (build_dirs from project config), bump-version.sh (version from project config), branch-align.sh, change-to-all-branches.sh.
- Project config schema in projects/README.md; lecoffreio.json as example.

**Pages affectées:**
- .cursor/agents/*.md, .cursor/rules/*.mdc, deploy/*.sh, projects/README.md, projects/lecoffreio.json, README.md, CLAUDE.md, config files.
2026-03-12 21:44:29 +01:00

191 lines
5.1 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)"
DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && 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 slug: .ia_project in repo root or IA_PROJECT env
PROJECT_SLUG=""
if [[ -f "$PROJECT_ROOT/.ia_project" ]]; then
PROJECT_SLUG="$(cat "$PROJECT_ROOT/.ia_project" | sed 's/[[:space:]]//g')"
fi
if [[ -z "$PROJECT_SLUG" && -n "${IA_PROJECT:-}" ]]; then
PROJECT_SLUG="$IA_PROJECT"
fi
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/<slug>.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/<slug>.json); skip if no config or no build_dirs
build_dirs=()
if [[ -n "$PROJECT_SLUG" && -f "$IA_DEV_ROOT/projects/${PROJECT_SLUG}.json" ]] && 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' "$IA_DEV_ROOT/projects/${PROJECT_SLUG}.json" 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 [[ ! -d "${repo_root}/${dir}" ]]; then
echo "[pousse][WARN] Skipping build ${dir} (directory not found)" >&2
continue
fi
echo "[pousse] Building ${dir}..."
(cd "${repo_root}/${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/<slug>.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