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
### 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
# dans le dépôt qui doit déployer
git submodule add https://figureslibres.io/gitea/bachir/gitploy.git gitploy
git submodule update --init
gitploy/setup
```
### En dépôt autonome
```bash
git clone gitea-figureslibres.io:bachir/gitploy.git
cd gitploy && ./setup # configure le dépôt parent courant
gitploy/setup # installe des alias git locaux (sans PATH)
```
`setup` installe des **alias git locaux** (`git deploy`, `git hotfix`) pointant
vers les scripts**aucune modification du `PATH`**. git n'exécutant jamais une
config versionnée tout seul (par sécurité), cette étape unique est nécessaire.
vers les scripts. git n'exécutant jamais une config versionnée tout seul (par
sécurité), cette étape est nécessaire à chaque clone — c'est le prix du submodule.
## 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) ;
2. merge `master``prod` ;
3. push atomique (dernière étape) ;
3. push de `prod` (dernière étape) ;
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,
**push atomique de tout à la fin** — tout ou rien) :
**push atomique de toutes les branches nommées à la fin** — tout ou rien) :
```bash
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) ;
2. bascule sur `prod`, applique et committe le correctif ;
3. rapatrie le correctif dans `master` (merge) ;
4. push atomique puis retour sur `master`.
3. rapatrie le correctif dans `master` en local (merge) ;
4. push de `prod` puis retour sur `master`.
Résultat : le correctif est sur `prod` **et** sur `master`, sans embarquer les
features non livrées de `master`.
Résultat : le correctif est sur `prod` (poussé) **et** dans ton `master` local,
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é
+18 -9
View File
@@ -13,7 +13,7 @@
# git config deploy.push true|false (défaut : false — push désactivé)
set -euo pipefail
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
DIR=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
# shellcheck source=_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=""
for b in "${BRANCHES[@]}"; do
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
seen="$seen $b"
done
# '$SOURCE' est autorisée dans la liste : elle sera poussée telle quelle (pas de merge).
current=$(git branch --show-current)
[ "$current" = "$SOURCE" ] || die "Place-toi sur '$SOURCE' pour déployer (git switch $SOURCE)."
assert_clean_git_state
@@ -55,10 +55,15 @@ fi
# ---- Confirmation ----------------------------------------------------------
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)"
[ -n "$merge_list" ] && merge_line="merge '$SOURCE' → $merge_list" || merge_line="aucun merge (push direct)"
confirm " • $commit_line
• merge '$SOURCE' → $list
$merge_line
• push : $push_line"
# ---- Sauvegarde de l'état pour rollback ------------------------------------
@@ -96,6 +101,10 @@ if [ "$dirty" = 1 ]; then
fi
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 merge --no-edit -q "$SOURCE" || fail "Conflit de merge sur '$b'."
echo "✓ '$b' à jour depuis '$SOURCE'."
@@ -106,14 +115,14 @@ echo "✓ Local OK : $list."
# ---- Push : dernière étape, atomique, jamais forcé -------------------------
if [ "$PUSH" = true ]; then
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 atomique ($list + '$SOURCE') → '$REMOTE'…"
git push --atomic "$REMOTE" "${BRANCHES[@]}" "$SOURCE" \
echo "→ Push atomique ($list) → '$REMOTE'…"
git push --atomic "$REMOTE" "${BRANCHES[@]}" \
|| { 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'."
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
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é)
set -euo pipefail
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
DIR=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
# shellcheck source=_deploy_lib.sh
. "$DIR/_deploy_lib.sh"
@@ -50,7 +50,7 @@ assert_clean_git_state
{ [ "$MSG_SET" = 1 ] && [ -n "$MSG" ]; } || die 'Message requis : -m "…".'
# ---- 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'
• application + commit sur '$BRANCH' : \"$MSG\"
• rapatriement (merge) dans '$SOURCE'
@@ -99,14 +99,14 @@ echo "✓ Local OK : hotfix sur '$BRANCH' + rapatrié dans '$SOURCE'."
# ---- Push : dernière étape, atomique, jamais forcé -------------------------
if [ "$PUSH" = true ]; then
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 atomique '$BRANCH' + '$SOURCE' → '$REMOTE'…"
git push --atomic "$REMOTE" "$BRANCH" "$SOURCE" \
|| { echo "✗ Échec du push. RIEN poussé (atomique). Local conservé." >&2; \
echo " Relance : git push --atomic $REMOTE $BRANCH $SOURCE" >&2; exit 1; }
echo "→ Push '$BRANCH' → '$REMOTE'…"
git push "$REMOTE" "$BRANCH" \
|| { echo "✗ Échec du push. RIEN poussé. Local conservé." >&2; \
echo " Relance : git push $REMOTE $BRANCH" >&2; exit 1; }
echo "✓ Poussé sur '$REMOTE'."
else
echo "→ (push désactivé) aurait fait : git push --atomic $REMOTE $BRANCH $SOURCE"
echo "→ (push désactivé) aurait fait : git push $REMOTE $BRANCH"
fi
echo "✓ Terminé. Tu es sur '$SOURCE'."