# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # configuration par défaut # doit être identique au contenu de pman.conf # les branches sont mergées dans cet ordre: # upstream --> develop --> [release -->] main --> dist # feature _/ hotfix _/ # branche upstream UPSTREAM= # branches de développement DEVELOP=develop FEATURE=wip/ # branche de préparation de release RELEASE=release- # branche de release MAIN=master TAG_PREFIX= TAG_SUFFIX= # branche de hotfix HOTFIX=hotfix- # branche de distribution DIST= # désactiver les releases automatiques? NOAUTO= CONFIG_VARS=( UPSTREAM DEVELOP FEATURE RELEASE MAIN HOTFIX DIST TAG_PREFIX TAG_SUFFIX NOAUTO ) ################################################################################ PMAN_BRANCHES=(UPSTREAM DEVELOP FEATURE MAIN DIST) PMAN_TOOL_PDEV=DEVELOP PMAN_TOOL_PWIP=FEATURE PMAN_TOOL_PMAIN=MAIN PMAN_TOOL_PDIST=DIST UPSTREAM_BASE=DEVELOP ; UPSTREAM_MERGE_FROM= ; UPSTREAM_MERGE_TO=DEVELOP ; UPSTREAM_PREL= ; UPSTREAM_DELETE= DEVELOP_BASE=MAIN ; DEVELOP_MERGE_FROM=FEATURE ; DEVELOP_MERGE_TO=MAIN ; DEVELOP_PREL=from ; DEVELOP_DELETE=to MAIN_BASE=DEVELOP ; MAIN_MERGE_FROM=DEVELOP ; MAIN_MERGE_TO=DIST ; MAIN_PREL=to ; MAIN_DELETE= DIST_BASE=MAIN ; DIST_MERGE_FROM=MAIN ; DIST_MERGE_TO= ; DIST_PREL= ; DIST_DELETE= FEATURE_BASE=DEVELOP ; FEATURE_MERGE_FROM= ; FEATURE_MERGE_TO=DEVELOP ; FEATURE_PREL= ; FEATURE_DELETE=from UPSTREAM_CREATE_FUNCTION=_create_upstream_action function get_base_branch() { # afficher la branche depuis laquelle créer la branche $1 # retourner 1 en cas d'erreur (pas de branche source) local branch="$1" infos [ -n "$branch" ] || return 1 infos="${branch^^}_BASE"; branch="${!infos}" [ -n "$branch" ] && echo "$branch" || return 1 } function get_merge_from_branch() { # afficher la branche depuis laquelle la branche $1 doit merger # retourner 1 en cas d'erreur (pas de branche source) local branch="$1" infos [ -n "$branch" ] || return 1 infos="${branch^^}_MERGE_FROM"; branch="${!infos}" [ -n "$branch" ] && echo "$branch" || return 1 } function get_merge_to_branch() { # afficher la branche dans laquelle la branche $1 doit merger # retourner 1 en cas d'erreur (pas de branche destination) local branch="$1" infos [ -n "$branch" ] || return 1 infos="${branch^^}_MERGE_TO"; branch="${!infos}" [ -n "$branch" ] && echo "$branch" || return 1 } function should_prel_merge() { # tester si la branche $1 doit être mergée avec prel dans la direction # $2(=to) local branch="$1" merge_dir="${2:-to}" infos [ -n "$branch" ] || return 1 infos="${branch^^}_PREL" [ "${!infos}" == "$merge_dir" ] } function should_delete_merged() { # tester si la branche $1 doit être supprimée après avoir été mergée dans la # direction $2(=to) local branch="$1" merge_dir="${2:-to}" infos [ -n "$branch" ] || return 1 infos="${branch^^}_DELETE" [ "${!infos}" == "$merge_dir" ] } : " # description des variables # * REF_BRANCH -- code de la branche de référence basé sur le nom de l'outil * RefBranch -- nom effectif de la branche si elle est définie dans .pman.conf, vide sinon * IfRefBranch -- nom effectif de la branche *si elle existe*, vide sinon * REF_UNIQUE -- si la branche de référence est unique. est vide pour les codes de branches multiples, telle que FEATURE * BASE_BRANCH -- branche de base à partir de laquelle créer la branche de référence * BaseBranch -- nom effectif de la branche de base si elle est définie dans .pman.conf, vide sinon * IfBaseBranch -- nom effectif de la branche de base *si elle existe*, vide sinon * MERGE_FROM -- code de la branche source à partir de laquelle la fusion est faite dans REF_BRANCH. vide si la branche n'a pas de source * MERGE_TO -- code de la branche destination dans laquelle la fusion est faite depuis REF_BRANCH. vide si la branche n'a pas de destination * MERGE_DIR -- direction de la fusion: 'from' si on fait REF_BRANCH --> MERGE_TO 'to' si on fait MERGE_FROM --> REF_BRANCH * PREL_MERGE -- si la fusion devrait se faire avec prel * DELETE_MERGED -- s'il faut supprimer la branche source après la fusion * MERGE_SRC -- code de la branche source pour la fusion, ou vide si la fusion n'est pas possible * MergeSrc -- nom effectif de la branche source si elle est définie dans .pman.conf * IfMergeSrc -- nom effectif de la branche source *si elle existe*, vide sinon * MERGE_DEST -- code de la branche destination pour la fusion, ou vide si la fusion n'est pas possible * MergeDest -- nom effectif de la branche destination si elle est définie dans .pman.conf * IfMergeDest -- nom effectif de la branche source *si elle existe*, vide sinon" function set_pman_vars() { RefBranch="${!REF_BRANCH}" case "$REF_BRANCH" in FEATURE|RELEASE|HOTFIX) REF_UNIQUE=;; *) REF_UNIQUE=1;; esac BASE_BRANCH=$(get_base_branch "$REF_BRANCH") [ -n "$BASE_BRANCH" ] && BaseBranch="${!BASE_BRANCH}" || BaseBranch= MERGE_FROM=$(get_merge_from_branch "$REF_BRANCH") MERGE_TO=$(get_merge_to_branch "$REF_BRANCH") if [ -n "$1" ]; then MERGE_DIR="$1" else MERGE_DIR=from fi PREL_MERGE=$(should_prel_merge "$REF_BRANCH" "$MERGE_DIR" && echo 1) DELETE_MERGED=$(should_delete_merged "$REF_BRANCH" "$MERGE_DIR" && echo 1) case "$MERGE_DIR" in from) MERGE_SRC="$REF_BRANCH" MERGE_DEST="$MERGE_TO" ;; to) MERGE_SRC="$MERGE_FROM" MERGE_DEST="$REF_BRANCH" ;; esac [ -n "$MERGE_SRC" ] && MergeSrc="${!MERGE_SRC}" || MergeSrc= [ -n "$MERGE_DEST" ] && MergeDest="${!MERGE_DEST}" || MergeDest= } ################################################################################ function _init_changelog() { setx date=date +%d/%m/%Y-%H:%M ac_set_tmpfile changelog echo >"$changelog" "\ Vérifiez et complétez la liste des changements le cas échéant. Un fichier vide annule l'opération Ces lignes ne seront pas incluses dans le fichier destination ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ " } function _filter_rel() { # enlever les commits "techniques" générés par ce script awk ' BEGIN { tech = 0 } tech == 0 && $0 ~ /^\+.*/ { tech = 1; next } tech == 1 && $0 ~ /^\|/ { next } tech == 1 && $0 ~ /^\+/ { tech = 0 } $0 !~ // { print } ' } function _filter_changes() { # enlever les commits "inutiles" pour générer le fichier CHANGES.md grep -vE '^([+|] )?[0-9a-f]+ modifs\.mineures sans commentaires$' | grep -vE '^([+|] )?[0-9a-f]+ (cosmetic|typo|bug|fix|maj projet|maj deps)$' } function _format_md() { awk ' $0 == "" || $0 ~ /^#/ { print; next } $1 == "+" { $1 = "*" $2 = "`" $2 "`" print; next } $1 == "|" { $1 = " *" $2 = "`" $2 "`" print; next } { $1 = "* `" $1 "`" print; next } ' } function _list_commits() { local source="${1:-$MergeSrc}" dest="${2:-$MergeDest}" mergebase setx mergebase=git merge-base "$dest" "$source" git log --oneline --graph --no-decorate "$mergebase..$source" | grep -vF '|\' | grep -vF '|/' | sed -r 's/^(\| )+\* +/| /; s/^\* +/+ /' | _filter_rel } function _show_diff() { local source="${1:-$MergeSrc}" dest="${2:-$MergeDest}" mergebase setx mergebase=git merge-base "$dest" "$source" git diff ${_sd_COLOR:+--color=$_sd_COLOR} "$mergebase..$source" } function _scripte() { echo >>"$script" echo "$comment$(qvals "$@")" >>"$script" } function _scripta() { [ $# -gt 0 ] && _scripte einfo "$*" cat >>"$script" } function _script_push_branches() { [ ${#push_branches[*]} -gt 0 ] || return [ -n "$Origin" ] || Origin=origin git_have_remote "$Origin" || return local branch cmd remote rbranch for branch in "${push_branches[@]}"; do if [[ "$branch" == *:* ]]; then cmd="$(qvals git push "$Origin" "$branch")" else setx remote=git_get_branch_remote "$branch" if [ "$remote" == "$Origin" ]; then setx rbranch=git_get_branch_merge "$branch" if [ -n "$rbranch" ]; then # pousser vers la branche distante existante cmd="$(qvals git push "$Origin" "$branch:${rbranch#refs/heads/}")" else # pas de branche distante: pousser et traquer cmd="$(qvals git push -u "$Origin" "$branch:$branch")" fi elif [ -n "$remote" ]; then # pousser vers un remote différent cmd="$(qvals git push "$Origin" "$branch:$branch")" else # pas de remote: pousser et traquer cmd="$(qvals git push -u "$Origin" "$branch:$branch")" fi fi _scripta <<<"$comment$cmd$or_die" done } function _script_push_tags() { local origin tag _scripte einfo "push tags" for tag in "${push_tags[@]}"; do origin="$Origin" [ -n "$origin" ] || origin=origin _scripta <"$config" 2>/dev/null [ -s "$config" ] || die "$ConfigBranch: aucune configuration trouvée sur cette branche" || return ConfigFile="$config" source "$ConfigFile" fi elif [ -f .pman.conf ]; then ConfigFile="$(pwd)/.pman.conf" source "$ConfigFile" else ConfigFile="$NULIBDIR/bash/src/pman.conf.sh" fi # S'assurer que nulib est dans le PATH pour que les scripts utilisateurs # puissent utiliser les outils fournis export PATH="$NULIBDIR/bin:$PATH" } ################################################################################ # Divers function resolve_should_push() { local quiet="$1" ShouldPush=1 if ! git_have_remote "$Origin" && [ -n "$Push" ]; then [ -n "$quiet" ] || enote "L'option --no-push a été forcée puisque ce dépôt n'a pas d'origine" ShouldPush= fi [ -z "$ShouldPush" ] && Push= } function _push_branches() { [ ${#push_branches[*]} -gt 0 ] || return [ -n "$Origin" ] || Origin=origin git_have_remote "$Origin" || return local -a cmds; local branch cmd remote rbranch for branch in "${push_branches[@]}"; do if [[ "$branch" == *:* ]]; then cmds+=("$(qvals git push "$Origin" "$branch")") else setx remote=git_get_branch_remote "$branch" if [ "$remote" == "$Origin" ]; then setx rbranch=git_get_branch_merge "$branch" if [ -n "$rbranch" ]; then # pousser vers la branche distante existante cmds+=("$(qvals git push "$Origin" "$branch:${rbranch#refs/heads/}")") else # pas de branche distante: pousser et traquer cmds+=("$(qvals git push -u "$Origin" "$branch:$branch")") fi elif [ -n "$remote" ]; then # pousser vers un remote différent cmds+=("$(qvals git push "$Origin" "$branch:$branch")") else # pas de remote: pousser et traquer cmds+=("$(qvals git push -u "$Origin" "$branch:$branch")") fi fi done [ -n "$Push" ] || enote "L'option --no-push étant utilisée, les opérations à effectuer sont simplement affichées" for cmd in "${cmds[@]}"; do einfo "$cmd" if [ -n "$Push" ]; then if ! eval "$cmd"; then ewarn "Une erreur s'est produite, les opérations seront simplement affichées" Push= fi fi done } ################################################################################ # Merge function _mscript_start() { >"$script" _scripta <"$script" _scripta <>"$changelog" "\ ## Release $Tag du $date " _list_commits | _filter_changes | _format_md >>"$changelog" "${EDITOR:-nano}" +7 "$changelog" [ -s "$changelog" ] || exit_with ewarn "Création de la release annulée" # créer la branche de release et basculer dessus _scripta "create branch $ReleaseBranch" <"\$tmpchanges" if [ -s CHANGES.md ]; then echo >>"\$tmpchanges" cat CHANGES.md >>"\$tmpchanges" fi cat "\$tmpchanges" >CHANGES.md rm -f "\$tmpchanges" git add CHANGES.md EOF # mettre à jour la version _scripta "update VERSION.txt" <VERSION.txt git add VERSION.txt EOF # Enregistrer les changements _scripta "commit" <Init changelog & version $Tag") EOF } function _rscript_merge_release_branch() { local dest="$1" tag="$2" # basculer sur la branche _scripta "switch to branch $dest" <Intégration de la branche $ReleaseBranch" --no-ff)$or_die EOF array_addu push_branches "$dest" # tagger la release if [ -n "$tag" ]; then _scripta "create tag $tag" <Intégration initiale de la branche $RefBranch" \ -srecursive -Xours --allow-unrelated-histories \ "$RefBranch" push_branches+=("$DevelopBranch") } function checkout_action() { local -a push_branches [ -n "$REF_UNIQUE" ] || resolve_unique_branch "$@" _ensure_ref_branch init if array_contains LocalBranches "$RefBranch"; then git checkout "$RefBranch" elif array_contains AllBranches "$RefBranch"; then enote "$RefBranch: une branche du même nom existe dans l'origine" ask_yesno "Voulez-vous basculer sur cette branche?" O || die git checkout "$RefBranch" else _ensure_base_branch resolve_should_push local create_function create_function="${REF_BRANCH}_CREATE_FUNCTION"; create_function="${!create_function}" [ -n "$create_function" ] || create_function=_create_default_action "$create_function" _push_branches fi } function ensure_merge_branches() { [ -n "$MergeSrc" ] || die "\ $RefBranch: configuration de fusion non trouvée: la branche $MERGE_SRC n'a pas été définie. Veuillez éditer le fichier .pman.conf" [ -n "$MergeDest" ] || die "\ $RefBranch: configuration de fusion non trouvée: la branche $MERGE_DEST n'a pas été définie. Veuillez éditer le fichier .pman.conf" local branches [ "$1" == -a ] && branches=AllBranches || branches=LocalBranches array_contains "$branches" "$MergeSrc" || die "$MergeSrc: branche source introuvable" array_contains "$branches" "$MergeDest" || die "$MergeDest: branche destination introuvable" } function _show_action() { local commits setx commits=_list_commits "$MergeSrc" "$MergeDest" if [ -n "$commits" ]; then if [ $ShowLevel -ge 2 ]; then { echo "\ # Commits à fusionner $MergeSrc --> $MergeDest $commits " _sd_COLOR=always _show_diff } | less -eRF else einfo "Commits à fusionner $MergeSrc --> $MergeDest" eecho "$commits" fi fi } function show_action() { git_check_cleancheckout || ewarn "$git_cleancheckout_DIRTY" [ -n "$REF_UNIQUE" ] || resolve_unique_branch "$@" ensure_merge_branches _show_action "$@" }