feat(deploy): generic orchestrator and deploy.sh entry
**Motivations:** - Single generic orchestration in ia_dev while business logic stays in each project repo **Root causes:** - N/A (evolution) **Correctifs:** - N/A **Evolutions:** - Add orchestrator.sh (deploy.hooks.phases or fallback deploy.deploy_script_path) - Add deploy.sh <project_id> <env> [options] as canonical entry from ia_dev root - run-project-hooks.sh execs orchestrator.sh for backward compatibility - change-to-all-branches.sh and deploy-by-script-to.sh invoke orchestrator.sh when IA_PROJECT_ID is set - Document orchestration in README.md and deploy/lib/README.md **Pages affectées:** - README.md, deploy/orchestrator.sh, deploy/deploy.sh, deploy/run-project-hooks.sh, deploy/change-to-all-branches.sh, deploy/deploy-by-script-to.sh, deploy/lib/README.md
This commit is contained in:
parent
418bfb044a
commit
0a9d6e001b
@ -40,6 +40,12 @@ Tous les scripts sont invoqués depuis la **racine de ia_dev** (ce dépôt).
|
|||||||
|
|
||||||
Les scripts dans `deploy/` **déploient et versionnent les projets configurés** (lecoffreio, enso, algo, etc.) dans leurs répertoires — ils ne déploient pas ia_dev. À lancer depuis la racine de ia_dev. Chaque script accepte en option un **project_id** en premier argument (ou `--project <id>` pour pousse.sh) pour cibler le projet ; sinon le projet est résolu par MAIL_TO ou AI_AGENT_TOKEN. Les chemins absolus dans `projects/<id>/conf.json` indiquent où se trouve chaque projet et ses scripts de déploiement.
|
Les scripts dans `deploy/` **déploient et versionnent les projets configurés** (lecoffreio, enso, algo, etc.) dans leurs répertoires — ils ne déploient pas ia_dev. À lancer depuis la racine de ia_dev. Chaque script accepte en option un **project_id** en premier argument (ou `--project <id>` pour pousse.sh) pour cibler le projet ; sinon le projet est résolu par MAIL_TO ou AI_AGENT_TOKEN. Les chemins absolus dans `projects/<id>/conf.json` indiquent où se trouve chaque projet et ses scripts de déploiement.
|
||||||
|
|
||||||
|
**Orchestration générique (métier dans le dépôt cible)** :
|
||||||
|
|
||||||
|
- **deploy.sh** : point d’entrée `./deploy/deploy.sh <project_id> <env> [options…]` — exporte `IA_PROJECT_ID`, puis exécute **orchestrator.sh** (même contrat que les scripts ci-dessous qui invoquent l’orchestrateur lorsque `IA_PROJECT_ID` est défini).
|
||||||
|
- **orchestrator.sh** : lit `deploy.hooks.phases` dans `conf.json` (chemins relatifs à `repository_root`) ; si `phases` est vide, exécute `deploy.deploy_script_path`. Le métier (Prisma, systemd, build distant, etc.) reste dans les scripts du projet.
|
||||||
|
- **run-project-hooks.sh** : délègue à **orchestrator.sh** (alias pour compatibilité avec les anciennes invocations).
|
||||||
|
|
||||||
- **bump-version.sh** : lit `projects/<id>/conf.json` (version.package_json_paths, version.splash_app_name) et met à jour VERSION + package.json du **projet configuré**. Invocation : `./deploy/bump-version.sh [project_id] <version> [message]` depuis la racine de ia_dev.
|
- **bump-version.sh** : lit `projects/<id>/conf.json` (version.package_json_paths, version.splash_app_name) et met à jour VERSION + package.json du **projet configuré**. Invocation : `./deploy/bump-version.sh [project_id] <version> [message]` depuis la racine de ia_dev.
|
||||||
- **deploy-by-script-to.sh** : checkout branche cible (pprod|prod), sync origin, exécute `deploy/scripts_v2/deploy.sh` du **projet configuré** (chemin dans conf), puis revient sur test. Invocation : `./deploy/deploy-by-script-to.sh [project_id] <pprod|prod>`.
|
- **deploy-by-script-to.sh** : checkout branche cible (pprod|prod), sync origin, exécute `deploy/scripts_v2/deploy.sh` du **projet configuré** (chemin dans conf), puis revient sur test. Invocation : `./deploy/deploy-by-script-to.sh [project_id] <pprod|prod>`.
|
||||||
- **pousse.sh** : build check, commit, push. Invocation : `./deploy/pousse.sh [project_id|--project <id>] [--remote <remote>] [--bump-version]` avec le message de commit sur STDIN.
|
- **pousse.sh** : build check, commit, push. Invocation : `./deploy/pousse.sh [project_id|--project <id>] [--remote <remote>] [--bump-version]` avec le message de commit sur STDIN.
|
||||||
|
|||||||
@ -47,8 +47,8 @@ echo "[change-to-all-branches] Aligning branches..."
|
|||||||
# scripts_v2 lives in the host project's deploy/ (not necessarily under ia_dev)
|
# scripts_v2 lives in the host project's deploy/ (not necessarily under ia_dev)
|
||||||
DEPLOY_SCRIPTS_V2="${PROJECT_ROOT}/deploy/scripts_v2"
|
DEPLOY_SCRIPTS_V2="${PROJECT_ROOT}/deploy/scripts_v2"
|
||||||
echo "[change-to-all-branches] Deploying test (--import-v1 --skipSetupHost, --no-sync-origin because we just pushed)..."
|
echo "[change-to-all-branches] Deploying test (--import-v1 --skipSetupHost, --no-sync-origin because we just pushed)..."
|
||||||
if [[ -n "${IA_PROJECT_ID:-}" && -x "${DEPLOY_DIR}/run-project-hooks.sh" ]]; then
|
if [[ -n "${IA_PROJECT_ID:-}" && -x "${DEPLOY_DIR}/orchestrator.sh" ]]; then
|
||||||
"${DEPLOY_DIR}/run-project-hooks.sh" test --import-v1 --skipSetupHost --no-sync-origin
|
"${DEPLOY_DIR}/orchestrator.sh" test --import-v1 --skipSetupHost --no-sync-origin
|
||||||
else
|
else
|
||||||
"${DEPLOY_SCRIPTS_V2}/deploy.sh" test --import-v1 --skipSetupHost --no-sync-origin
|
"${DEPLOY_SCRIPTS_V2}/deploy.sh" test --import-v1 --skipSetupHost --no-sync-origin
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -74,8 +74,8 @@ git fetch origin
|
|||||||
git reset --hard "origin/${TARGET_BRANCH}"
|
git reset --hard "origin/${TARGET_BRANCH}"
|
||||||
|
|
||||||
echo "[deploy-by-script-to] Step 4/5: deploy ${TARGET_BRANCH} (--import-v1 --skipSetupHost)..."
|
echo "[deploy-by-script-to] Step 4/5: deploy ${TARGET_BRANCH} (--import-v1 --skipSetupHost)..."
|
||||||
if [[ -n "${IA_PROJECT_ID:-}" && -x "${DEPLOY_IA}/run-project-hooks.sh" ]]; then
|
if [[ -n "${IA_PROJECT_ID:-}" && -x "${DEPLOY_IA}/orchestrator.sh" ]]; then
|
||||||
"${DEPLOY_IA}/run-project-hooks.sh" "$TARGET_BRANCH" --import-v1 --skipSetupHost
|
"${DEPLOY_IA}/orchestrator.sh" "$TARGET_BRANCH" --import-v1 --skipSetupHost
|
||||||
else
|
else
|
||||||
deploy_script="$PROJECT_ROOT/deploy/scripts_v2/deploy.sh"
|
deploy_script="$PROJECT_ROOT/deploy/scripts_v2/deploy.sh"
|
||||||
if [[ -n "${PROJECT_CONFIG_PATH:-}" && -f "${PROJECT_CONFIG_PATH:-}" ]] && command -v jq >/dev/null 2>&1; then
|
if [[ -n "${PROJECT_CONFIG_PATH:-}" && -f "${PROJECT_CONFIG_PATH:-}" ]] && command -v jq >/dev/null 2>&1; then
|
||||||
|
|||||||
27
deploy/deploy.sh
Executable file
27
deploy/deploy.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Generic deploy entry from ia_dev: sets IA_PROJECT_ID and runs the orchestrator.
|
||||||
|
# Business logic remains in the target repository (deploy_script_path, hooks.phases).
|
||||||
|
# Usage (from ia_dev root): ./deploy/deploy.sh <project_id> <env> [options...]
|
||||||
|
# Example: ./deploy/deploy.sh lecoffreio test --import-v1 --skipSetupHost
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
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 [[ $# -lt 2 ]]; then
|
||||||
|
echo "[deploy][ERROR] Missing arguments" >&2
|
||||||
|
echo "Usage: $0 <project_id> <env> [options passed to each phase / fallback deploy script]" >&2
|
||||||
|
echo "Example: $0 lecoffreio test --import-v1 --skipSetupHost" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONF="${IA_DEV_ROOT}/projects/${1}/conf.json"
|
||||||
|
if [[ ! -f "$CONF" ]]; then
|
||||||
|
echo "[deploy][ERROR] No conf for project '${1}': ${CONF}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export IA_PROJECT_ID="$1"
|
||||||
|
shift
|
||||||
|
exec "$DEPLOY_DIR/orchestrator.sh" "$@"
|
||||||
@ -12,3 +12,8 @@ Optional `deploy_script_tee_log_if_requested <project_root> <log_subdir>` — re
|
|||||||
## Policy
|
## Policy
|
||||||
|
|
||||||
Project-specific logic (Prisma, systemd unit names, remote app layout, LeCoffre domains) stays under each repository’s `deploy/scripts_v2/`. Only transport/logging helpers live here.
|
Project-specific logic (Prisma, systemd unit names, remote app layout, LeCoffre domains) stays under each repository’s `deploy/scripts_v2/`. Only transport/logging helpers live here.
|
||||||
|
|
||||||
|
## Orchestration (`../orchestrator.sh`, `../deploy.sh`)
|
||||||
|
|
||||||
|
- **`deploy.sh`** : `./deploy/deploy.sh <project_id> <env> [args]` — generic entry; business scripts live in the target repo (`deploy_script_path` / `hooks.phases`).
|
||||||
|
- **`orchestrator.sh`** : runs phases or fallback script; **`run-project-hooks.sh`** execs it for backward compatibility.
|
||||||
|
|||||||
74
deploy/orchestrator.sh
Executable file
74
deploy/orchestrator.sh
Executable file
@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Generic deploy orchestrator: runs deploy.hooks.phases from projects/<id>/conf.json (paths relative to repository_root).
|
||||||
|
# If phases is empty or missing, exec deploy.deploy_script_path with the same arguments.
|
||||||
|
# Business logic (Prisma, systemd, remote layout) stays in each project's scripts under repository_root.
|
||||||
|
# Usage: orchestrator.sh <env> [options passed to each phase / fallback script]
|
||||||
|
# Requires: IA_PROJECT_ID, IA_DEV_ROOT (or re-exec from project root like change-to-all-branches).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
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)"
|
||||||
|
|
||||||
|
_ORCH_TAG="[orchestrator]"
|
||||||
|
|
||||||
|
if [[ -z "${IA_PROJECT_ID:-}" ]]; then
|
||||||
|
echo "${_ORCH_TAG}[ERROR] IA_PROJECT_ID is not set" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=../lib/project_config.sh
|
||||||
|
source "${IA_DEV_ROOT}/lib/project_config.sh"
|
||||||
|
# shellcheck source=../lib/project_git_root_from_conf.sh
|
||||||
|
source "${IA_DEV_ROOT}/lib/project_git_root_from_conf.sh"
|
||||||
|
ia_dev_resolve_project_git_root
|
||||||
|
REPO_ROOT="${IA_PROJECT_GIT_ROOT:-}"
|
||||||
|
if [[ -z "$REPO_ROOT" || ! -d "$REPO_ROOT" ]]; then
|
||||||
|
echo "${_ORCH_TAG}[ERROR] Could not resolve repository root for project ${IA_PROJECT_ID}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONF="${PROJECT_CONFIG_PATH:-}"
|
||||||
|
if [[ -z "$CONF" || ! -f "$CONF" ]]; then
|
||||||
|
echo "${_ORCH_TAG}[ERROR] Missing conf: ${CONF:-}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
echo "${_ORCH_TAG}[ERROR] jq is required to read deploy.hooks.phases" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SECRETS_PATH_CFG="$(jq -r '.deploy.secrets_path // empty' "$CONF")"
|
||||||
|
if [[ -n "$SECRETS_PATH_CFG" && "$SECRETS_PATH_CFG" != "null" && -d "$SECRETS_PATH_CFG" ]]; then
|
||||||
|
export SECRETS_BASE="$SECRETS_PATH_CFG"
|
||||||
|
export LECOFFRE_SECRETS_BASE="$SECRETS_PATH_CFG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEPLOY_SCRIPT_PATH="$(jq -r '.deploy.deploy_script_path // empty' "$CONF")"
|
||||||
|
if [[ -z "$DEPLOY_SCRIPT_PATH" || ! -f "$DEPLOY_SCRIPT_PATH" ]]; then
|
||||||
|
echo "${_ORCH_TAG}[ERROR] deploy.deploy_script_path missing or not a file: ${DEPLOY_SCRIPT_PATH:-}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PHASE_COUNT="$(jq '.deploy.hooks.phases // [] | length' "$CONF")"
|
||||||
|
if [[ "$PHASE_COUNT" == "0" ]]; then
|
||||||
|
exec bash "$DEPLOY_SCRIPT_PATH" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mapfile -t PHASE_SCRIPTS < <(jq -r '.deploy.hooks.phases[]? | if type == "string" then . elif type == "object" and (.run | type == "string") then .run else empty end' "$CONF")
|
||||||
|
|
||||||
|
if [[ ${#PHASE_SCRIPTS[@]} -eq 0 ]]; then
|
||||||
|
exec bash "$DEPLOY_SCRIPT_PATH" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for rel in "${PHASE_SCRIPTS[@]}"; do
|
||||||
|
[[ -z "$rel" ]] && continue
|
||||||
|
phase_path="${REPO_ROOT}/${rel}"
|
||||||
|
if [[ ! -f "$phase_path" ]]; then
|
||||||
|
echo "${_ORCH_TAG}[ERROR] Phase script not found: ${phase_path}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "${_ORCH_TAG} Running: ${rel} $*"
|
||||||
|
bash "$phase_path" "$@"
|
||||||
|
done
|
||||||
@ -1,71 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Run deploy.hooks.phases from projects/<id>/conf.json (paths relative to repository_root).
|
# Backward-compatible alias: delegates to orchestrator.sh (generic deploy orchestrator).
|
||||||
# If phases is empty or missing, exec deploy.deploy_script_path with the same arguments.
|
|
||||||
# Usage: run-project-hooks.sh <env> [options passed to each phase / fallback script]
|
# Usage: run-project-hooks.sh <env> [options passed to each phase / fallback script]
|
||||||
# Requires: IA_PROJECT_ID, IA_DEV_ROOT (or re-exec from project root like change-to-all-branches).
|
# Requires: IA_PROJECT_ID (set by caller or by deploy.sh).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_REAL="$(readlink -f "${BASH_SOURCE[0]:-$0}" 2>/dev/null || realpath "${BASH_SOURCE[0]:-$0}" 2>/dev/null || echo "${BASH_SOURCE[0]:-$0}")"
|
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)"
|
DEPLOY_DIR="$(cd "$(dirname "$SCRIPT_REAL")" && pwd)"
|
||||||
IA_DEV_ROOT="$(cd "$DEPLOY_DIR/.." && pwd)"
|
exec "$DEPLOY_DIR/orchestrator.sh" "$@"
|
||||||
|
|
||||||
if [[ -z "${IA_PROJECT_ID:-}" ]]; then
|
|
||||||
echo "[run-project-hooks][ERROR] IA_PROJECT_ID is not set" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# shellcheck source=../lib/project_config.sh
|
|
||||||
source "${IA_DEV_ROOT}/lib/project_config.sh"
|
|
||||||
# shellcheck source=../lib/project_git_root_from_conf.sh
|
|
||||||
source "${IA_DEV_ROOT}/lib/project_git_root_from_conf.sh"
|
|
||||||
ia_dev_resolve_project_git_root
|
|
||||||
REPO_ROOT="${IA_PROJECT_GIT_ROOT:-}"
|
|
||||||
if [[ -z "$REPO_ROOT" || ! -d "$REPO_ROOT" ]]; then
|
|
||||||
echo "[run-project-hooks][ERROR] Could not resolve repository root for project ${IA_PROJECT_ID}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
CONF="${PROJECT_CONFIG_PATH:-}"
|
|
||||||
if [[ -z "$CONF" || ! -f "$CONF" ]]; then
|
|
||||||
echo "[run-project-hooks][ERROR] Missing conf: ${CONF:-}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v jq >/dev/null 2>&1; then
|
|
||||||
echo "[run-project-hooks][ERROR] jq is required to read deploy.hooks.phases" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SECRETS_PATH_CFG="$(jq -r '.deploy.secrets_path // empty' "$CONF")"
|
|
||||||
if [[ -n "$SECRETS_PATH_CFG" && "$SECRETS_PATH_CFG" != "null" && -d "$SECRETS_PATH_CFG" ]]; then
|
|
||||||
export SECRETS_BASE="$SECRETS_PATH_CFG"
|
|
||||||
export LECOFFRE_SECRETS_BASE="$SECRETS_PATH_CFG"
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEPLOY_SCRIPT_PATH="$(jq -r '.deploy.deploy_script_path // empty' "$CONF")"
|
|
||||||
if [[ -z "$DEPLOY_SCRIPT_PATH" || ! -f "$DEPLOY_SCRIPT_PATH" ]]; then
|
|
||||||
echo "[run-project-hooks][ERROR] deploy.deploy_script_path missing or not a file: ${DEPLOY_SCRIPT_PATH:-}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
PHASE_COUNT="$(jq '.deploy.hooks.phases // [] | length' "$CONF")"
|
|
||||||
if [[ "$PHASE_COUNT" == "0" ]]; then
|
|
||||||
exec bash "$DEPLOY_SCRIPT_PATH" "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mapfile -t PHASE_SCRIPTS < <(jq -r '.deploy.hooks.phases[]? | if type == "string" then . elif type == "object" and (.run | type == "string") then .run else empty end' "$CONF")
|
|
||||||
|
|
||||||
if [[ ${#PHASE_SCRIPTS[@]} -eq 0 ]]; then
|
|
||||||
exec bash "$DEPLOY_SCRIPT_PATH" "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for rel in "${PHASE_SCRIPTS[@]}"; do
|
|
||||||
[[ -z "$rel" ]] && continue
|
|
||||||
phase_path="${REPO_ROOT}/${rel}"
|
|
||||||
if [[ ! -f "$phase_path" ]]; then
|
|
||||||
echo "[run-project-hooks][ERROR] Phase script not found: ${phase_path}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "[run-project-hooks] Running: ${rel} $*"
|
|
||||||
bash "$phase_path" "$@"
|
|
||||||
done
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user