Git Pre Commit Hook for Phing PHP Build Select Files

682 views Asked by At

I want to use a pre-commit hook to check my codefiles before commit them to git. For checking I will use Phing (a PHP build tool).

Here is my pre_commit_hook (MacOS):

#!/bin/sh
echo "Checking files before commit!"
phing -f pre_commit_build.xml || exit 1

And here is the build file:

<?xml version="1.0" encoding="UTF-8"?>
<project name="MyApplication" default="main" basedir=".">

    <fileset id="php_project_files" dir="app">
        <include name="*.php" />
        <include name="**/*.php" />
    </fileset>

    <target name="main">

        <echo message="+------------------------------------------+"/>
        <echo message="|                                          |"/>
        <echo message="| Pre_Commit_Build                         |"/>
        <echo message="|                                          |"/>
        <echo message="+------------------------------------------+"/>

        <phingcall target="check_file_names" />
        <phingcall target="check_syntax" />
        <phingcall target="check_coding_guideline" />
        <phingcall target="unit_test" />

    </target>

    ...

    <target name="check_file_names">

        <property name="failure" value="0" />

        <adhoc-task name="check_filename"><![CDATA[
            class CheckFileName extends Task {

            /** Any filesets of files that should be appended. */
            private $filesets = array();

            /**
             * Nested creator, adds a set of files (nested <fileset> attribute).
             * This is for when you don't care what order files get appended.
             * @return FileSet
             */
             function createFileSet() {
                 $num = array_push($this->filesets, new FileSet());
                 return $this->filesets[$num-1];
             }

             function main() {

                 // append any files in filesets
                 foreach($this->filesets as $fs) {
                     try {
                         $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
                         foreach ($files as $file) {

                             if (preg_match('/^[a-z0-9-._]+$/', $file)) {
                                 $this->log("Checked: " . $file);
                             } else {
                                 $this->log("Error: " . $file . " : Allowed are only a-z 0-9 . - _", Project::MSG_ERR);
                                 $this->project->setProperty('failure', '1');
                             }
                          }
                      } catch (BuildException $be) {
                          $this->log($be->getMessage(), Project::MSG_WARN);
                      }
                  }    
              }
          }

      ]]></adhoc-task>

      <echo message="+------------------------------------------+"/>
      <echo message="|                                          |"/>
      <echo message="| Check PHP Filenames                      |"/>
      <echo message="|                                          |"/>
      <echo message="+------------------------------------------+"/>

          <check_filename>
              <fileset refid="php_project_files" />
          </check_filename>

          <echo message="Failure ${failure}" />

    ...

    <target name="check_syntax">

        <phplint level="verbose" haltonfailure="false">
            <fileset refid="php_project_files" />
        </phplint>

    </target>

    ...

</project>

Currently git starts the build process and ALL files of the folder 'app' will be checked (not only those I want to commit). After the process although there will found some fails (i.e. violation of coding guideline) the build process finished and the files will be committed (but they haven't to in this case).

Here are my questions:

  1. How can I tell Phing to use only those files which I want to commit. Or: How can I transfer the information about the files to commit from git to Phing?

  2. How can I "collect" the fails in the targets of the build process (i.e. wrong filenames or violation of a coding guideline) and control, if the build process has to fail.

  3. If one or more target fails, how can I send a return value (1) to git, so that git stops the commit?

  4. How can I define in an adhoc-task that it fails (I have tried it with the property ${failure}?

Thanks a lot for your support!

Stefan

2

There are 2 answers

0
cweiske On BEST ANSWER

About "only what I want to commit":

In our commit hook, we checkout the files to a temporary staging area and examine those. That's the only way to verify only the staged changes. (Also think of git add -p).

Return value: Just let your build fail. phing exists with a non-0 status code.

0
stefan On

RETURN VALUE:

If my task has failed I will store this in the property ${task_failed} (failed=1).

Than I let the build fail by

<if>
    <equals arg1="${task_failed}" arg2="1" />
    <then> 
        <fail message="Task failed" />
    </then>
</if>

COLLECTING UNCOMMITTED FILES:

Thanks to @cweiske I have created a task to collect my uncommitted files:

<target name="collect_uncommited_files">
    <exec command="git diff-index --name-only --cached --diff-filter=ACMR HEAD" output="uncommitted_files.txt" />
    <fileset id="uncommitted_files" dir="./" includesfiles="uncommited_files.txt">
    </fileset>
</target>

I filter uncollected files with git diff-index and store the result into the external file uncommited_files.txt. Than I will load this file to create a fileset with my uncommitted files. (This would also work, when I store the result into a property and build my file set with include.)

In my case it seems to be a better way instead to copy all uncommitted files into a separate directory like done in @cweiske:checkout the files, because I don't want to move some MB around (I have to commit some very large files).