Koha Test Wiki MW Canasta on Koha Portainer

Test major Koha Wiki changes or bug fixes here without fear of breaking the production wiki.

For the current Koha Wiki, visit https://wiki.koha-community.org .

Tips and tricks

From Koha Test Wiki MW Canasta on Koha Portainer
Jump to navigation Jump to search

git tips and tricks

This page collect all git tips and tricks Koha developers have found


git

Hooks

A nice feature of git is hooks.

A hook is a small (or large) script that is executed before or after running some git command.

Hooks are located in the .git/hooks directory; To enable one simply add a file named after the hook point you wish to use to that directory and ensure it is executable.

pre-commit

This pre-commit hook will check for common coding issues before allowing you to complete a commit:

#!/usr/bin/perl

use Modern::Perl;

use File::Basename;
use Term::ANSIColor qw(colored);

$ENV{LOG} = "test";

my $nb_errors = 0;
my @compiled_css = ( "staff-global", "opac" );

for my $filepath (`git diff --cached --name-only`) {
    chomp $filepath;
    next if not -f $filepath;

    my @file_infos = fileparse( $filepath, qr/\.[^.]*/ );

    if ( grep( $file_infos[0], @compiled_css ) && $file_infos[2] eq ".css" ) {
        say colored( "You don't want to commit $filepath", 'red' );
        $nb_errors++;
    }

    if ( $file_infos[2] =~ /^.pl|^.pm$/ ) {
        system(qq{/usr/bin/perl -wc $filepath}) == 0
          or say "\n" and $nb_errors++;
    } 
    elsif ( $file_infos[2] =~ /^.tt$/ ) {

        #TODO
    }
    elsif ( $file_infos[2] =~ /^.js$/ ) {

        #TODO
    }
}

my $filepath;
for my $l ( split '\n', `git diff-index -p -M --cached HEAD` ) {
    if ( $l =~ /^diff --git a\/([^ ]*) .*$/ ) {
        $filepath = $1;
    }
    if ( $l =~ /console.log/ ) {
        say colored( "$filepath contains console.log ($l)", 'red' );
        $nb_errors++;
    }
    elsif ( $l =~ /^\+ *warn Data::Dumper::Dumper / ) {
        say colored( "$filepath contains warn Data::Dumper::Dumper ($l)",
            'red' );
        $nb_errors++;
    }

    # This one could be uncommented when Koha will have the Logger module
    elsif ( $l =~ /^\+ *warn / ) {
        say "$filepath contains warn ($l)";
        $nb_errors++;
    }

    elsif( $l =~ /\+ *\t/) { # fail also if there is some space before the tab
        say colored( "$filepath contains a tab, must use 4 spaces ($l)",
            'red' );
        $nb_errors++;
    }

    elsif ( $l =~ m/^<<<<<<</ or $l =~ m/^>>>>>>>/ or $l =~ m/^=======/ ) {
        say colored( "$filepath contains $& ($l)", 'red' );
    }
    elsif ( $l =~ /(\+|-)\s*use/ ) {
        say colored( "Patch add or remove a use in $filepath: $l", 'red' );
        say
"You should run xt/find-undefined-subroutines.pl to check if nothing is broken";
        $nb_errors++;
    }
}

if ($nb_errors) {
    say "\nAre you sure you want to commit ?";
    say "You can commit with the --no-verify argument";
    exit 1;
}

say colored( "Success, pre-commit checks passed", 'green' );

exit 0;

The above script checks that:

  • All the perl scripts you want to commit are compiling (perl -wc)
  • You are not committing compiled CSS files (CSS generated from SCSS).
  • There are no trailing conflict markers (<<<<, >>>>>, =====)
  • There are no trailing `warn` or `warn Data::Dumper::Dumper` additions
  • There is no file called {something}.log

pre-applypatch

The pre-commit hook can also be useful as a pre-applypatch hook to catch issues with others bugs when applying via either `git am` or `git bz`.

pre-push

This pre-push hook will check for common coding mistakes before allowing the RM or RMaint to push to their community branch:

#!/usr/bin/perl

use Modern::Perl;
use List::MoreUtils qw( any );
use Term::ANSIColor qw(colored);

my $RELEASE   = '19.06.x';                                            # The branch you are managing/maintaining
my $REMOTE    = 'upstream';                                           # The name you gave to the remote for git.koha-community.org
my $SIGNATURE = q|Martin Renvoize <martin.renvoize@ptfs-europe.com>|; # Your signature for sign-off lines

my $remote   = $ARGV[0];
my $minor    = $RELEASE;
my $upstream = ( $RELEASE =~ m{^\d\d\.(05|11)\.x$} ) ? $RELEASE : 'master';
$minor =~ s/x//g;
if ( $remote eq $REMOTE ) {

    say "Running pre-push hook\n";

    # Check branch
    my $current_branch = `git branch | grep \\* | cut -d ' ' -f2`;
    chomp $current_branch;
    if ( $current_branch ne $RELEASE ) {
        say colored( "Current branch is not $RELEASE", 'red' );
        exit 1;
    }

    # Check commits exist
    my @commits = `git rev-list $REMOTE/$upstream..HEAD`;
    unless (@commits) {
        say colored( 'Hum... no commits to push?', 'green' );
        exit 1;
    }

    # Check commit messages are well formed
    my @errors;
    for my $commit (@commits) {

         say colored("Working on $commit", 'green');

        my $commit_message = `git log --format=%s -1 $commit`;
        if ( $commit_message !~ m|^Bug\s\d{4,5}: | ) {
            push @errors,
              colored( "Does not start with 'Bug XXXXX: ' - $commit", 'red' );
        }

        if ( $commit_message =~ m|DO NOT PUSH| ) {
            push @errors,
                colored( "Comit contains DO NOT PUSH - $commit", 'red');
        }

        if ( $commit_message =~ m|DBRev|i
            and not $commit_message =~ m|DBRev $minor| )
        {
            warn $commit_message;
            push @errors,
              colored( "DBRev is wrong, should start with 'DBRev $RELEASE'",
                'red' );
        }

        my $body = `git log --format=%b -1 $commit`;
        $body =~ s|\n\s*|\n|g;
        if ( $body !~ m|\n?Signed-off-by: $SIGNATURE| ) {
            push @errors, colored( "No Signed-off-by line - $commit", 'red' );
        }

        my $author_name = `git log --format=%an -1 $commit`;
        if ( $author_name eq 'John Doe' ) {
            push @errors, colored( "Bad author name - $commit", 'red' );
        }

        my $author_email = `git log --format=%ae -1 $commit`;
        if ( $author_email eq 'you@example.com' ) {
            push @errors, colored( "Bad author email - $commit", 'red' );
        }
    }

    # Check for remaining atomicupdate files
    my @atomicupdate_files =
`git show HEAD:installer/data/mysql/atomicupdate/|tr -s '\\n' | grep -v '^tree'|grep -v 'README'|grep -v skeleton`;
    chomp for @atomicupdate_files;
    for my $atomic (@atomicupdate_files) {
        push @errors, colored( "Atomicupdate file exists - $atomic", 'red' );
    }

    my @dbrev_files = `git show HEAD:installer/data/mysql/db_revs/|tr -s '\\n' | grep -v '^tree'`;
    chomp for @dbrev_files;
    for my $f ( @dbrev_files ) {
        my $mode = (stat("installer/data/mysql/db_revs/$f"))[2] & 07777;
        push @errors, "dbrev file is missing the +x flag - $f" if $mode != 0775;
    }

    # Check for missing CSS Compiles
    my $compiled = `yarn build && yarn build --view=opac`;
    my @css_changes = `git diff --name-only`;
    chomp for @css_changes;
    for my $css_change (@css_changes) {
        push @errors, colored( "CSS file changes exist - $css_change", 'red' );
    }

    # Report any errors
    if (@errors) {
        say colored($_, 'red') for @errors;
        exit 1;
    }

    say colored("Passed tests, pushing...", 'green');

}

exit 0;

This hook will prevent:

  • to push a local branch that is not named "$RELEASE"
  • to push commits that are not signed off by the RM/RMaint
  • to push commits that do not start with "Bug XXXXX: "
  • to push DBRev commits that do not correctly formatted ("DBRev $RELEASE")
  • to push atomicupdate files

Display the branch you're on

If you want to permanently display the branch you're on, edit your .bashrc file and add the following at the end:

#prompt git
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=0
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUPSTREAM="verbose"
PS1='\D{%H:%M} \[\033[1;35m\]\w$(__git_ps1 " \[\033[1;34m\](%s)")\[\033[0m\]\$ '

your display will look like this:

17:47 ~/koha.dev/koha-community (new/bug_7190 $%)$ 

displaying the directory you're on, the branch, and some informations about the status of your working directory


Git aliases to simplify command tasks

If you have Git-BZ installed, you can add the following aliases to your .gitconfig file to make applying a patch and reattaching it with your signoff a 1-command affair:

[alias]
      so = !sh -c 'prove t xt && git commit --amend -s && git bz attach -e $1 HEAD' -
      qa = !sh -c 'git fetch origin master && git checkout -b bug$1-qa origin/master && git bz apply $1' -
      gr = log --graph --full-history --all --color --pretty=tformat:"%x1b[31m%h%x09%x1b[32m%x1b[0m%x20%d%s%x20%x1b[33m(%an)%x1b[0m"
      qa2 = "!f() { c=`expr $1 - 1`; git filter-repo --message-callback 'return message + b\"\\nSigned-off-by: Full Name <email>\"' --refs HEAD~$c^..; }; f"

You may need to install git filter-repo first.

git so ####

Runs automated tests, signs off on the patch and attaches it to the bug report, obsoleting the unsigned patch.

git qa ####

Fetches the latest master from git.koha-community.org, creates a new branch off that for the bug you're testing, then applies the patch from Bugzilla

git gr

Not BZ-specific, but this draws a character-mode graph showing the branch structure and displays it through your pager.

git qa2 N

Not BZ-specific, this alias will add your signature to N patches. git qa2 3 => deal with 3 patches