Compare commits

..
3 Commits
3 changed files with 68 additions and 34 deletions
+42 -17
View File
@@ -21,25 +21,37 @@ main**, puis rapatrie automatiquement le correctif dans le dev.
## Installation ## Installation
### En submodule (recommandé) ### Au niveau utilisateur (recommandé)
gitploy est un *outil* : on l'installe une fois, tous les dépôts en profitent.
```bash
# 1. cloner une fois dans un emplacement stable
git clone gitea-figureslibres.io:bachir/gitploy.git ~/.local/share/gitploy
# 2. exposer les sous-commandes (via un dossier du PATH, ex. ~/.local/bin)
ln -sf ~/.local/share/gitploy/git-deploy ~/.local/bin/git-deploy
ln -sf ~/.local/share/gitploy/git-hotfix ~/.local/bin/git-hotfix
# 3. complétion (une fois)
echo 'source "$HOME/.local/share/gitploy/deploy-completion.bash"' >> ~/.bash_completion
```
`git deploy` / `git hotfix` sont alors disponibles dans **tous** tes dépôts,
sans alias ni `setup`. Mise à jour globale : `git -C ~/.local/share/gitploy pull`.
### En submodule (pour figer une version par projet)
```bash ```bash
# dans le dépôt qui doit déployer # dans le dépôt qui doit déployer
git submodule add https://figureslibres.io/gitea/bachir/gitploy.git gitploy git submodule add https://figureslibres.io/gitea/bachir/gitploy.git gitploy
git submodule update --init git submodule update --init
gitploy/setup gitploy/setup # installe des alias git locaux (sans PATH)
```
### En dépôt autonome
```bash
git clone gitea-figureslibres.io:bachir/gitploy.git
cd gitploy && ./setup # configure le dépôt parent courant
``` ```
`setup` installe des **alias git locaux** (`git deploy`, `git hotfix`) pointant `setup` installe des **alias git locaux** (`git deploy`, `git hotfix`) pointant
vers les scripts**aucune modification du `PATH`**. git n'exécutant jamais une vers les scripts. git n'exécutant jamais une config versionnée tout seul (par
config versionnée tout seul (par sécurité), cette étape unique est nécessaire. sécurité), cette étape est nécessaire à chaque clone — c'est le prix du submodule.
## Configuration ## Configuration
@@ -69,11 +81,22 @@ git deploy gitea prod -m "ajout du bloc contact"
1. commit sur `master` (message obligatoire s'il y a des modifs) ; 1. commit sur `master` (message obligatoire s'il y a des modifs) ;
2. merge `master``prod` ; 2. merge `master``prod` ;
3. push atomique (dernière étape) ; 3. push de `prod` (dernière étape) ;
4. retour sur `master`. 4. retour sur `master`.
> `git deploy` ne pousse **que les branches nommées**, jamais `master` implicitement.
> Ça évite qu'un bare repo serveur (dont le hook n'attend qu'une branche précise)
> rejette le push.
>
> Pour sauvegarder `master` sur un remote central, inclus-le explicitement dans la
> liste — il sera poussé tel quel (sans merge) :
>
> ```bash
> git deploy gitea master prod stage -m "message"
> ```
Plusieurs environnements en une commande (commit unique, merges enchaînés, Plusieurs environnements en une commande (commit unique, merges enchaînés,
**push atomique de tout à la fin** — tout ou rien) : **push atomique de toutes les branches nommées à la fin** — tout ou rien) :
```bash ```bash
git deploy gitea prod stage -m "ajout du bloc contact" git deploy gitea prod stage -m "ajout du bloc contact"
@@ -88,11 +111,13 @@ git hotfix gitea prod -m "fix: lien du menu"
1. met tes modifs de côté (stash) ; 1. met tes modifs de côté (stash) ;
2. bascule sur `prod`, applique et committe le correctif ; 2. bascule sur `prod`, applique et committe le correctif ;
3. rapatrie le correctif dans `master` (merge) ; 3. rapatrie le correctif dans `master` en local (merge) ;
4. push atomique puis retour sur `master`. 4. push de `prod` puis retour sur `master`.
Résultat : le correctif est sur `prod` **et** sur `master`, sans embarquer les Résultat : le correctif est sur `prod` (poussé) **et** dans ton `master` local,
features non livrées de `master`. sans embarquer les features non livrées de `master`. Comme `git deploy`, `git
hotfix` ne pousse **que** la branche d'env, pas `master` : ton `master` distant
se mettra à jour quand tu le pousseras explicitement.
## Sûreté ## Sûreté
+18 -9
View File
@@ -13,7 +13,7 @@
# git config deploy.push true|false (défaut : false — push désactivé) # git config deploy.push true|false (défaut : false — push désactivé)
set -euo pipefail set -euo pipefail
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) DIR=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
# shellcheck source=_deploy_lib.sh # shellcheck source=_deploy_lib.sh
. "$DIR/_deploy_lib.sh" . "$DIR/_deploy_lib.sh"
@@ -40,10 +40,10 @@ git remote get-url "$REMOTE" >/dev/null 2>&1 || die "Remote inconnu : $REMOTE"
seen="" seen=""
for b in "${BRANCHES[@]}"; do for b in "${BRANCHES[@]}"; do
git show-ref --verify --quiet "refs/heads/$b" || die "Branche locale inconnue : $b" git show-ref --verify --quiet "refs/heads/$b" || die "Branche locale inconnue : $b"
[ "$b" != "$SOURCE" ] || die "'$SOURCE' est la branche de dev, pas une branche de déploiement."
case " $seen " in *" $b "*) die "Branche indiquée en double : $b" ;; esac case " $seen " in *" $b "*) die "Branche indiquée en double : $b" ;; esac
seen="$seen $b" seen="$seen $b"
done done
# '$SOURCE' est autorisée dans la liste : elle sera poussée telle quelle (pas de merge).
current=$(git branch --show-current) current=$(git branch --show-current)
[ "$current" = "$SOURCE" ] || die "Place-toi sur '$SOURCE' pour déployer (git switch $SOURCE)." [ "$current" = "$SOURCE" ] || die "Place-toi sur '$SOURCE' pour déployer (git switch $SOURCE)."
assert_clean_git_state assert_clean_git_state
@@ -55,10 +55,15 @@ fi
# ---- Confirmation ---------------------------------------------------------- # ---- Confirmation ----------------------------------------------------------
list="${BRANCHES[*]}" list="${BRANCHES[*]}"
if [ "$PUSH" = true ]; then push_line="OUI vers '$REMOTE' ($list + '$SOURCE', atomique)"; else push_line="NON (désactivé)"; fi # branches réellement fusionnées = toutes sauf SOURCE (qui est poussée telle quelle)
merge_list=""
for b in "${BRANCHES[@]}"; do [ "$b" != "$SOURCE" ] && merge_list="$merge_list $b"; done
merge_list="${merge_list# }"
if [ "$PUSH" = true ]; then push_line="OUI vers '$REMOTE' ($list, atomique)"; else push_line="NON (désactivé)"; fi
[ "$dirty" = 1 ] && commit_line="commit sur '$SOURCE' : \"$MSG\"" || commit_line="aucun commit (arbre propre)" [ "$dirty" = 1 ] && commit_line="commit sur '$SOURCE' : \"$MSG\"" || commit_line="aucun commit (arbre propre)"
[ -n "$merge_list" ] && merge_line="merge '$SOURCE' → $merge_list" || merge_line="aucun merge (push direct)"
confirm " • $commit_line confirm " • $commit_line
• merge '$SOURCE' → $list $merge_line
• push : $push_line" • push : $push_line"
# ---- Sauvegarde de l'état pour rollback ------------------------------------ # ---- Sauvegarde de l'état pour rollback ------------------------------------
@@ -96,6 +101,10 @@ if [ "$dirty" = 1 ]; then
fi fi
for b in "${BRANCHES[@]}"; do for b in "${BRANCHES[@]}"; do
if [ "$b" = "$SOURCE" ]; then
echo "✓ '$b' (dev) incluse au push telle quelle."
continue
fi
git switch -q "$b" || fail "Impossible de basculer sur '$b'." git switch -q "$b" || fail "Impossible de basculer sur '$b'."
git merge --no-edit -q "$SOURCE" || fail "Conflit de merge sur '$b'." git merge --no-edit -q "$SOURCE" || fail "Conflit de merge sur '$b'."
echo "✓ '$b' à jour depuis '$SOURCE'." echo "✓ '$b' à jour depuis '$SOURCE'."
@@ -106,14 +115,14 @@ echo "✓ Local OK : $list."
# ---- Push : dernière étape, atomique, jamais forcé ------------------------- # ---- Push : dernière étape, atomique, jamais forcé -------------------------
if [ "$PUSH" = true ]; then if [ "$PUSH" = true ]; then
echo "→ Vérification du push (dry-run)…" echo "→ Vérification du push (dry-run)…"
git push --atomic --dry-run "$REMOTE" "${BRANCHES[@]}" "$SOURCE" \ git push --atomic --dry-run "$REMOTE" "${BRANCHES[@]}" \
|| { echo "✗ Push rejeté au dry-run (non fast-forward ?). RIEN poussé. Local conservé." >&2; exit 1; } || { echo "✗ Push rejeté au dry-run (non fast-forward ?). RIEN poussé. Local conservé." >&2; exit 1; }
echo "→ Push atomique ($list + '$SOURCE') → '$REMOTE'…" echo "→ Push atomique ($list) → '$REMOTE'…"
git push --atomic "$REMOTE" "${BRANCHES[@]}" "$SOURCE" \ git push --atomic "$REMOTE" "${BRANCHES[@]}" \
|| { echo "✗ Échec du push. RIEN poussé (atomique). Local conservé." >&2; \ || { echo "✗ Échec du push. RIEN poussé (atomique). Local conservé." >&2; \
echo " Relance : git push --atomic $REMOTE $list $SOURCE" >&2; exit 1; } echo " Relance : git push --atomic $REMOTE $list" >&2; exit 1; }
echo "✓ Poussé sur '$REMOTE'." echo "✓ Poussé sur '$REMOTE'."
else else
echo "→ (push désactivé) aurait fait : git push --atomic $REMOTE $list $SOURCE" echo "→ (push désactivé) aurait fait : git push --atomic $REMOTE $list"
fi fi
echo "✓ Terminé. Tu es sur '$SOURCE'." echo "✓ Terminé. Tu es sur '$SOURCE'."
+8 -8
View File
@@ -18,7 +18,7 @@
# git config deploy.push true|false (défaut : false — push désactivé) # git config deploy.push true|false (défaut : false — push désactivé)
set -euo pipefail set -euo pipefail
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) DIR=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
# shellcheck source=_deploy_lib.sh # shellcheck source=_deploy_lib.sh
. "$DIR/_deploy_lib.sh" . "$DIR/_deploy_lib.sh"
@@ -50,7 +50,7 @@ assert_clean_git_state
{ [ "$MSG_SET" = 1 ] && [ -n "$MSG" ]; } || die 'Message requis : -m "…".' { [ "$MSG_SET" = 1 ] && [ -n "$MSG" ]; } || die 'Message requis : -m "…".'
# ---- Confirmation ---------------------------------------------------------- # ---- Confirmation ----------------------------------------------------------
if [ "$PUSH" = true ]; then push_line="OUI vers '$REMOTE' ('$BRANCH' + '$SOURCE', atomique)"; else push_line="NON (désactivé)"; fi if [ "$PUSH" = true ]; then push_line="OUI vers '$REMOTE' ('$BRANCH')"; else push_line="NON (désactivé)"; fi
confirm " • stash des modifs de '$SOURCE' confirm " • stash des modifs de '$SOURCE'
• application + commit sur '$BRANCH' : \"$MSG\" • application + commit sur '$BRANCH' : \"$MSG\"
• rapatriement (merge) dans '$SOURCE' • rapatriement (merge) dans '$SOURCE'
@@ -99,14 +99,14 @@ echo "✓ Local OK : hotfix sur '$BRANCH' + rapatrié dans '$SOURCE'."
# ---- Push : dernière étape, atomique, jamais forcé ------------------------- # ---- Push : dernière étape, atomique, jamais forcé -------------------------
if [ "$PUSH" = true ]; then if [ "$PUSH" = true ]; then
echo "→ Vérification du push (dry-run)…" echo "→ Vérification du push (dry-run)…"
git push --atomic --dry-run "$REMOTE" "$BRANCH" "$SOURCE" \ git push --dry-run "$REMOTE" "$BRANCH" \
|| { echo "✗ Push rejeté au dry-run (non fast-forward ?). RIEN poussé. Local conservé." >&2; exit 1; } || { echo "✗ Push rejeté au dry-run (non fast-forward ?). RIEN poussé. Local conservé." >&2; exit 1; }
echo "→ Push atomique '$BRANCH' + '$SOURCE' → '$REMOTE'…" echo "→ Push '$BRANCH' → '$REMOTE'…"
git push --atomic "$REMOTE" "$BRANCH" "$SOURCE" \ git push "$REMOTE" "$BRANCH" \
|| { echo "✗ Échec du push. RIEN poussé (atomique). Local conservé." >&2; \ || { echo "✗ Échec du push. RIEN poussé. Local conservé." >&2; \
echo " Relance : git push --atomic $REMOTE $BRANCH $SOURCE" >&2; exit 1; } echo " Relance : git push $REMOTE $BRANCH" >&2; exit 1; }
echo "✓ Poussé sur '$REMOTE'." echo "✓ Poussé sur '$REMOTE'."
else else
echo "→ (push désactivé) aurait fait : git push --atomic $REMOTE $BRANCH $SOURCE" echo "→ (push désactivé) aurait fait : git push $REMOTE $BRANCH"
fi fi
echo "✓ Terminé. Tu es sur '$SOURCE'." echo "✓ Terminé. Tu es sur '$SOURCE'."