Background – Due to business requirements, we need to maintain a PROTO warehouse for the data structure definition of the front and back end students. However, in the process of using the warehouse, proTO file format errors often occur, resulting in the crash of the corresponding pages of the system. Therefore, we intend to enable the pre-commit verification scheme in the PROto warehouse to reduce the probability of such events.

What is the pre – commit?

Pre-commit may not be familiar, but most of us have enjoyed the convenience it brings. Pre-commit is a hook that comes with Git. It can iterate, inspect, or perform other operations on the committed content before committing.

As one of the most common examples, when developing JS projects, if we are using community provided scaffolding, we will be equipped with ESLint, and then we may fail to commit because some code style does not conform to THE ESLint specification.

This is what you can do with pre-commit, which ensures that the code submitted to the repository is consistent in style. Git provides the following hooks: prepare-commit-msg, post-commit, pre-rebase, etc. There are hooks at almost every stage from code submission to code push that we can trigger on demand.

How to trigger a pre-commit

At the root of every Git repository is a hidden.git folder with an internal folder called “hooks” that, as the name implies, holds hooks. Git hook template, which contains some built-in hook cases for your reference. After we write our own hook methods, we just need to remove the suffix of. Sample.

Tip: Any properly named executable script will work — you can write them in Ruby or Python, or some other language. — Custom git-git hooks

How to write a usable hook script

  • Identify object file

Before you can perform validation, you should first need to validate the object file for validation (the modified or added file). Git’s diff method is used to find out the list of files that have been modified or added.

#! /bin/bash
STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.proto')

if [[ $STAGE_FILES = ""]].then
    echo "No Proto files to check"   
    exit 0
fi
Copy the code
  • File content reading

If there is a proto file in the modified file, the next process will proceed. We first print out the detected change file for the submitter’s confirmation, followed by the verification process.

echo "$STAGE_FILESTo check"

for proto in $STAGE_FILES;do
    PROTO_TEXT=$(cat $proto)
    echo "$protoCheck in..."
done;

exit 0
Copy the code
  • File content is pushed to the server
request=`curl -s -H "Content-type: application/json" -H "X-Proto-Commit: precommit" -X POST -d '{"proto_text":"'$PROTO_TEXT'","name":"'$proto'"}' http://localhost:7777/api/proto/preCommit`
Copy the code
  • Server return processing
STATUS=`echo -e "'$request'" | grep "error"`
if [[ $STATUS = ""]].then
    echo "$protoCheck done..."   
else
    echo $STATUS
    exit 1
fi
Copy the code

We will take the list of change files obtained in the first step as the body of the loop, read out the contents of the PROto file of each change, and send it to the server responsible for providing detection capability by means of curl, and wait for the server to return the detection result.

It looks like a simple pre-commit-Proto capability is complete, but in practice, it turns out that some proTO files submitted to the detector will definitely report an error, even if the file itself has no error, the server will still return an error.

After several tests, the problem was finally identified as the encoding format. Because the encode function of Curl has limited support for Chinese, we decided to upload the whole ProTO file to the server for detection to ensure the absolute correctness of detection content.

How do I use curl to upload a proto file?

Since reading the contents of the pushed file directly cannot achieve the desired effect, we tried to upload the whole Proto file to the server for parsing.

- request=`curl -s -H "Content-type: application/json" -H "X-Proto-Commit: precommit" -X POST -d '{"proto_text":"'$PROTO_TEXT'","name":"'$proto'"}' http://localhost:7777/api/proto/preCommit`
+ request=`curl -k -s -F "file=@$proto" http://localhost:7777/api/proto/preCommit`
Copy the code

We just need to fine-tune the execution statement following the request variable to upload the proto file to the specified location. There are several parameters to note:

  • -f: uploads binary files to the server.
  • -k: specifies to skip SSL detection. (Optional, added because the example is not an HTTPS interface)
  • -s: Silent mode, output nothing (optional, depends on your needs)

Once the file has been successfully uploaded, the submission side of the code is done, and the rest is to see what the server side detects. If the test passes, exit the pre-commit and continue the commit action; otherwise, proto will be prompted for the wrong part of the file and the code commit will be interrupted.

How do I enable pre-commit on a new clone

At this point, you may still have a big question mark in mind. The main premise of the whole article is to modify the contents of the.git folder, but since it is a Git project, it must mean that someone other than yourself can run the project, in which case the pre-commit fails. Because the.git folder is local and cannot be stored in the repository, we need to use another method to load the pre-commit footsteps into their local.git/hooks.

#! /bin/bash
cp pre-commit .git/hooks/pre-commit
chmod 777 .git/hooks/pre-commit
echo 'Protobuf precheck initialization completed'
exit 0
Copy the code

Let’s read the above code block line by line, starting with declaring the bash script. Then copy the ‘pre-commit’ file from the project root directory to the specified path ‘.git/hooks/pre-commit’.

Git commit: ‘.git/hooks/pre-commit’ : 777 (read, write, execute)

Afterword.

At this point, a simple pre-commit hook has been developed and fired, and all the other hooks that Git provides are pretty much the same. You just need to select the hooks that fit your needs and replace the script you developed with the hooks folder. The complete code is attached below

init.sh

#! /bin/bash
cp pre-commit .git/hooks/pre-commit
chmod 777 .git/hooks/pre-commit
echo 'Protobuf precheck initialization completed'
exit 0

pre-commit.sh

#! /bin/bash
STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.proto')

if [[ $STAGE_FILES = ""]].then
    echo "No Proto files to check"   
    exit 0
fi

echo "$STAGE_FILESTo check"

for proto in $STAGE_FILES;do
    PROTO_TEXT=$(cat $proto)
    echo "$protoCheck in..."
    request=`curl -k -s -F "file=@$proto" http://localhost:7777/api/proto/preCommit`
    CODE=`echo -e "'$request'" | grep "code" | awk -F ":" '{print $2}' | awk -F "," '{print $1}'`
    if [[ $CODE> 0]];then
        echo "Pre-check service failure. Please try again."
        exit 1
    fi
    STATUS=`echo -e "'$request'" | grep "error"`
    if [[ $STATUS = ""]].then
        echo "$protoCheck done."
    else
        echo $STATUS
        exit 1
    fi
done;

exit 0
Copy the code