Gumulka

ein blog

Nov 22, 2019

Meine git-hooks

Generelle Struktur

Da ich einige Skripte für viele bis fast alle Projekte benötigen kann, mag ich es einen globalen Ordner für git-hooks zu haben.

Wenn man die Dokumentation anguckt, dann gibt es die Möglichkeit mit

git config --global core.hooksPath ~/.githooks/

alle hooks in meinem Homeverzeichnis zu haben. Dieses hat jetzt nur ein Problem. Ich verliere all hooks, welche ich local haben möchte.

Die beste Alternative, welche ich dazu bis jetzt gefunden habe war:

Ach, dann mach in dem entsprechendem Repo einfach ein ´git config core.hooksPath .git/hooks/´

Aber das möchte ich nicht machen. Ich verliere dann meine anderen globalen Hooks, sobald ich ein Skript Lokal haben möchte.

Meine Möglichkeit das zu umgehen sieht also das folgende vor:

#!/bin/bash

for hook in applypatch-msg pre-applypatch post-applypatch pre-commit pre-merge-commit prepare-commit-msg commit-msg post-commit pre-rebase post-checkout post-merge pre-push pre-receive update post-receive post-update push-to-checkout pre-auto-gc post-rewrite sendemail-validate fsmonitor-watchman p4-pre-submit post-index-change
do
    if [ ! -e ${hook} ]; then
        echo "#!/bin/bash" > ${hook}
        echo "" >> ${hook}
        echo 'GIT_BASE_DIR=$(git rev-parse --show-toplevel)' >> ${hook}
        echo "exitcode=0" >> ${hook}
        echo "" >> ${hook}
        echo "if [ -x \${GIT_BASE_DIR}/.git/hooks/${hook} ]; then" >> ${hook}
        echo "    \$GIT_BASE_DIR/.git/hooks/${hook} \$@ || exitcode=\$?" >> ${hook}
        echo "fi" >> ${hook}
        echo "" >> ${hook}
        echo "# Please insert your checks here." >> ${hook}
        echo "" >> ${hook}
        echo "exit \${exitcode}" >> ${hook}
        chmod a+x ${hook}
    fi
done

Dieses Skript erstellt für jeden möglichen hook ein neues Skript, welches zuerst im localen .git/hooks Verzeichnis nachguckt ob ein Skript existiert und es ausführt.

Ein paar Sachen auf die dabei zu achten sind:

  1. Git kann Parameter übergeben (die meisten Skripte)
  2. Git kann Informationen in das Skript reinpipen (pre-push, pre-receive, post-receive)
  3. Git kann eine Ausgabe erwarten (pre-receive, update, post-receive, post-update, fsmonitor-watchman)

Meine Lösung dafür ist:

  1. $@ übergibt alle Parameter des Skriptes an das Unterskript
  2. Siehe pre-push
  3. Ich benutze sie einfach nicht. Hier sind es hauptsächlich Server-Skripte, welche bei mir nicht eingesetzt werden.

Damit habe ich jetzt globale Skripte, welche keinen Unterschied zu localen Skripten darstellen. Nur kann ich sie noch erweitern und genau das habe ich für einige auch gemacht.

commit-msg

Rechtschreibung

Meinen commit-msg hook habe ich von hier übernommen. Ich habe es nur um das grep erweitert um die Kommentare von der Prüfung auszuschließen.

ASPELL=$(which aspell)
if [ $? -ne 0 ]; then
    echo "Aspell not installed - unable to check spelling" >&2
else
    WORDS=$(grep -v -e "^#" "$1" | $ASPELL list )
fi
if [ -n "$WORDS" ]; then
    echo -e "\e[1;33mPossible spelling errors found in commit message. Use git commit --amend to change the message.\nPossible mispelled words: " $WORDS ".\e[0m" >&2
fi

Ein einfaches Skript, welches guckt, ob die Commit Message Rechtschreibfehler enthält, da sich ja doch ab und zu welche einschleichen.

pre-commit

Standartskripte

Hier benutzte ich das Beispiel-Skript, da es eine vernünftige Grundfunktionalität besitzt, welche ich eigentlich gerne überall hätte. Nur den letzten Befehl habe ich etwas überarbeitet indem ich das exec entfernt habe um danach noch weiter schreiben zu können.

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=$(git hash-object -t tree /dev/null)
fi

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$(git config --bool hooks.allownonascii)" != "true" ] &&
    # Note that the use of brackets around a tr range is ok here, (it's
    # even required, for portability to Solaris 10's /usr/bin/tr), since
    # the square bracket bytes happen to fall in the designated range.
    test $(git diff --cached --name-only --diff-filter=A -z $against |
      LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
    echo -e "\e[1;31mCommiting on master is not allowed!\e[0m"
    echo -e "To allow commit on master for this repo execute:\n"
    echo -e "\tgit config --local --add hooks.allownonascii true\n"
    exitcode=1
fi

# If there are whitespace errors, print the offending file names and fail.
git diff-index --check --cached $against -- || exitcode=$?

Master schützen

Es gibt allerdings noch eine kleine Erweiterung. Ich möchte nicht unbedingt auf master commiten, da ich denke ein jeder kann und sollte mit feature und development branches arbeiten, also verbiete ich es mir einfach generell.

# Check where out HEAD is at
if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
        if [ "$(git config --bool --get hooks.allowcommitonmaster)" != "true" ]; then
                echo -e "\e[1;31mCommiting on master is not allowed!\e[0m"
                echo -e "To allow commit on master for this repo execute:\n"
                echo -e "\tgit config --local --add hooks.allowcommitonmaster true\n"
                exitcode=1
        fi
fi

In manchen Repositories ist es allerdings unerlässlich und damit ich den Befehl dafür niemals vergesse schreibe ich ihn gleich in die Fehlermeldung mit rein.

Python linter

Da ich in letzter Zeit viel in Python programmiere, habe ich zuerst angefangen für viele Projekte einen Linter im CI-File zu installieren, aber ich würde den check auch gerne schon vorher haben. Also filtere ich alle python Dateien raus, welche im Commit vorkommen, und sollte dieses nicht leer sein, dann werden nur diese durch den linter durchgeschickt:

# Do a linting for all modified python files.
pythonfiles=$(git diff --name-only --cached | grep .py | xargs)
if [ "${pythonfiles}" != "" ]; then
    # color output, dont print gobal score, and use numcpu threads
    pylint --persistent=n --jobs=0 --score=n --output-format=colorized ${pythonfiles} || exitcode=$?
fi

pre-push

Skript-Probleme

Bash übergibt Standartmäßig alle nicht genutzten Parameter der Standarteingabe an das aufgerufene Skript. Was für unseren Standartworkflow genau das ist, was wir benötigen. Sollte man jedoch einmal selbst auch die Eingaben verarbeiten wollen benötigt man etwas mehr. Ich kann jetzt leider nicht die Eingaben einfach doppelt verarbeiten, sondern muss sie in einer temporären Variable speichern. Dieses sieht dann folgendermaßen aus:

#!/bin/bash

OPERANDS=$(cat)

GIT_DIR=$(git rev-parse --show-toplevel)

if [ -x ${GIT_DIR}/.git/hooks/pre-push ]; then
    echo "${OPERANDS}" | $GIT_DIR/.git/hooks/pre-push || exitcode=$?
fi

testing

Was ich nett fand war die Möglichkeit teure pre-commit skripte erst bei pre-push auszuführen. Aber das muss ich erst noch ausbauen und die Programmierumgebung/Sprache entdecken.

Irgendwas in der Richtung wird da aber noch kommen.

Server abstellen

Es gibt auf meiner Arbeit einen git Server, welcher obsolet geworden ist, da die meisten Projekte auf einen anderen umgezogen sind. Eigentlich habe ich alle Repositories umgestellt, aber falls es nicht der Fall sein sollte, dann wird hier verhindert, auf genau diesen zu puschen. (Beispiel auf den Umzug von github zu gitlab erstellt)

if [[ $2 =~ "github.com:gumulka88" ]]; then
    echo -e "\e[1;31mThou shall not push to $2!\e[0m"
    echo "Change url to the new server with:"
    newurl=$(echo "$2" | sed 's/github.com:gumulka88/gitlab.com:gumulka/g')
    echo -e "\n\tgit remote set-url $1 ${newurl}\n"
    exitcode=1
fi

README und LICENSE

Ich mache häufiger mal ein ganz kleines Repo um irgendein Skript zu speichern, welches ich ich erstellt habe, und Monate später komme ich darauf zurück und denke mir:

Was wollte ich noch einmal damit genau und wie kann ich es ändern?

oder aber:

Was waren noch mal die Vorbedingungen für diesen Code?

Hier wäre es doch super, wenn ich eine README angelegt hätte. Oder aber ich finde irgendwo im Netz ein tolles Projekt, welches aber leider nicht mehr weiter entwickelt wird und keine Lizenz hat und es rechtlich schwierig ist, das einfach so zu benutzen im Firmenumfeld. Also lass mich doch dafür sorgen, dass wenigstens ich immer eine Lizenz habe. Das muss nicht beim ersten Commit passieren, wohl aber, wenn ich es veröffentliche.

Ich habe schon die Variable GIT_BASE_DIR, welche auf mein Stammverzeichnis zeigt. Jetzt muss ich nur noch gucken, ob die Dateien existieren.

for checking in README LICENSE; do
    lower=$(echo "${checking}" | tr '[:upper:]' '[:lower:]')
    if  [ "$(ls $GIT_BASE_DIR | grep -i ${checking})" == "" ] ; then
            if [ "$(git config --bool --get hooks.pushwithout${lower})" != "true" ]; then
                    echo -e "\e[1;31mYou must have a ${checking}!\e[0m"
                    echo -e "To allow pushing without a ${checking} for this repo execute:\n"
                    echo -e "\tgit config --local --add hooks.pushwithout${lower} true\n"
                    exitcode=1
            fi
    fi
done
posted at 17:50  ·   ·  git  bash  git-hooks

Feb 19, 2019

Automatisches Erstellen

Zuerst habe ich mir überlegt wie ich es wohl schaffen könnte mit Hooks die Webseite automatisch auszuliefern, aber die Sache, dass Git mir nicht erlaubt in einem Bare repo Sachen auszuchecken und ich in einem ausgecheckten Repo nicht puschen kann macht es etwas komplizierter.

Da ich seit Monaten schon verschiedene Runner für Gitlab auf Arbeit betreibe, war es einfacher einen auf meinem Webserver zu installieren und ihm Schreibrechte auf dem Ordner mit meinem Blog zu geben.

Dazu musste ich nur das Makefile um ein Target für den Server erweitern, einen Runner installieren und ein CI Skript schreiben.

Anbei die Ergänzungen am Makefile und mein CI Skript:

PUBLISHDIR=/var/www/blog

server-publish:
    $(PELICAN) $(INPUTDIR) -o $(PUBLISHDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
publish:
    script:
        - rm -rf /var/www/blog/drafts/
        - make server-publish

Feb 19, 2019

Ein weiterer Blogversuch

Ich versuche einmal wieder einen Blog zu schreiben.

Dieses Mal mit Markdown und pelican. Ich habe mir dazu das Template von Blue Penguin etwas angepasst, da mir Grün besser gefällt als Blau, aber ich muss da noch einiges machen. Wahrscheinlich wird es noch ein paar mal wechseln.

Gerade überlege ich, wie ich die Veröffentlichung mittels git-hooks hinbekomme. Wenn ich es fertig habe, dann werde ich sicherlich darüber schreiben.