Automating development deployment tasks with Git hooks

Source: Internet
Author: User
Tags exit in unpack how to use git using git git hooks

Automating development deployment tasks with Git hooks

Offers: Zstack community

Objective

Version control, which is one of the core requirements of modern software development. With it, software projects can safely track code changes and perform multiple operations such as backtracking, integrity checking, and collaborative development. In various version control software, git is one of the most popular software in recent years, its de-centralized architecture and the speed of source change Exchange is favored by many developers.

Among git the many advantages, the most useful is its flexibility. With the "hooks" (hook) system, developers and administrators can specify git to execute specific scripts under different events and actions.

This article will introduce the basic ideas and usage of git hooks and demonstrate how to automate tasks in your environment. The operating system used in this article is Ubuntu 14.04 Server Edition, theoretically any system that can run git can do it in the same way.

Pre-conditions

First, you have to install the server first git . Users of Ubuntu 14.04 can view this tutorial to learn how to install git on Ubuntu 14.04.

Second, you should be able to perform basic git operations. If you think you're not familiar with git, take a look at this git getting Started tutorial.

Please continue reading after the above conditions have been reached.

The basic idea of Git hooks

The concept of Git hooks is quite simple, and it is designed to be implemented for a single requirement. In the development of a shared project (or multi-person co-development project), team members need to ensure that their coding style is unified, that the deployment is uniform, and so on (often this scenario is covered by git users), which creates a lot of repetitive work.

Git Hooks is event-based (event-based). When you execute a specific git instruction, the software checks for the corresponding script from the directory under the GIT repository hooks and executes it if there is one.

Some scripts are executed before the action is executed, and this "antecedent script" can be used to implement the functions of code specification unification, integrity checking, environment building, etc. Some scripts are executed after the event, and this "down script" can be used to implement such functions as code deployment, error correction of permissions (Git has a bit of a lack of functionality in this regard).

In general, git hooks can implement policy enforcement, ensure consistency, environment control, deployment task handling, and many other functions.

Scott Chacon divides hooks into the following types in his pro git book:

    • Client hook: This type of hook is invoked on the submitter (Committer) 's computer for execution. This type of hook is divided into the following categories:

      • Code Submission related Workflow Hook: Submit a class hook action before and after the code commits, usually used for running integrity checks, submission information generation, information content validation and other functions, can also be used to send notifications.
      • Email related workflow Hook:email hooks are primarily used for code patches submitted using email. Projects such as the Linux kernel, which are submitted by email for patching, can use such hooks. The work style is similar to that of submitting hooks, and the project maintainer can use such hooks to perform the patching action directly.
      • Other classes: Include code merging, checkout (check out), rebase, rewrite (rewrite), and cleanup of the software warehouse.
    • Server-Side hooks: This type of hook acts on the server side and is typically used to receive push, deployed on the server where the project's GIT Repository backbone (main) resides. Chacon the server-side hooks into two categories:

      • Accept Trigger class: Performs an action before or after a push is received by the server, which is often used for checking, and a post-trigger is often used for deployment.
      • Update: Similar to a previous trigger, but the update class hook is a branch (branch) that acts as an object, executing code before each branch update passes.

The above classification helps us to create a holistic concept of hooks to understand what kind of events it can use. Of course, to be able to actually use it, but also need to do hands-on, debugging.

Some hooks can accept parameters. In other words, when git invokes the hook's script, we can pass some data to the script. The list of available hooks is as follows:

Hook name trigger Command Description number and description of parameters
Applypatch-msg ' Git am ' A message that can be submitted when a commit is edited. Typically used to verify or correct the information submitted by a patch to meet the project criteria. (1) file name containing the pre-commit information
Pre-applypatch ' Git am ' Although the name of the hook is "pre-patched", the actual invocation time is after patching and before changing the commit. Exiting in a state other than 0 causes the change to become the uncommitted state. Can be used to check the state of the code tree before actually making a commit. No
Post-applypatch ' Git am ' The call time of this hook is after patching, commit is completed after commit. Therefore, this hook cannot be used to cancel a process, but primarily for notification. No
Pre-commit ' Git commit ' The time to invoke this hook is before the commit message is obtained. If you exit in a state other than 0, this commit will be canceled. Primarily used to check the commit itself (not the message) No
Prepare-commit-msg ' Git commit ' This hook is called when the default commit message is received and before the commit message editor is started. The return result of non-0 will cancel this commit. This hook can be used to force the specified commit message to be applied. 1. The file name that contains the commit message. 2. The source of the commit message (message, template, merge, squash, or commit). 3. Commit SHA-1 (in the case of an existing commit).
Commit-msg ' Git commit ' Can be used to modify the contents of a message after a message is submitted or to return a failed commit of message. The return result of non-0 will cancel this commit. (1) The name of the file containing the message content.
Post-commit ' Git commit ' This hook is called after commit is completed and cannot be used to return a commit. Mainly used for notifications. No
Pre-rebase ' Git rebase ' Called when executing rebase, which can be used to interrupt unwanted rebase. 1. Upstream of this fork. 2. Rebase branch (without this parameter if Rebase is the current branch)
Post-checkout ' Git checkout ' and ' Git clone ' Called when checkout is called after updating the work tree, or after executing git clone. It is primarily used to verify the environment, display changes, and configure the environment. 1. Ref of the previous head. 2. Ref for new head. 3. A label that indicates whether it is a branch checkout or a file checkout.
Post-merge ' git merge ' or ' Git pull ' Called after the merge, cannot be used to cancel the merge. Can be used for actions such as permission actions that Git cannot perform. (1) A label that indicates whether the merge is labeled as squash at a time.
Pre-push ' Git push ' Called before the remote push is moved. In addition to carrying parameters, this hook also gives stdin the following information: "" (there is a space between each item). This information can be used to do some checking, for example, if the local SHA1 is 40 zeros, then this push is a delete operation, and if the remote SHA1 is 40 zeros, it is a new branch. The return result of non-0 cancels this push. 1. Name of the remote destination. 2. Location of the remote target.
Pre-receive Remote repo for ' git-receive-pack ' This hook is called before the remote repo update is just before the push ref. The return result of non-0 interrupts the process. Although this hook does not carry parameters, it will give stdin the following information: "". No
Update Remote repo for ' git-receive-pack ' This hook is called at the remote repo every time ref is push (not every push). Can be used to meet the requirements of "all commits can be fast forward". 1. The updated ref name. 2. The old object name. 3. The new object name.
Post-receive Remote repo for ' git-receive-pack ' This hook is called when all ref is updated on the remote repo and the push operation. This hook does not carry parameters, but it can receive information from stdin, receiving the format "". Because the call to the hook is made after the update, it cannot be used to terminate the process. No
Post-update Remote repo for ' git-receive-pack ' This hook is only executed once after all ref has been push. It is similar to post-receive, but does not receive old values and new values. Mainly used for notifications. Each push repo generates an argument with the name of the ref
Pre-auto-gc ' Git Gc–auto ' Used to do some checking before cleaning the repo automatically. No
Post-rewrite ' Git commit–amend ', ' git-rebase ' This hook is called when the git command rewrite (rewrite) is already in the commit data. In addition to the parameters it carries, this hook also receives information from stdin in the format "". The name of the command triggering the hook (amend or rebase)

Here are a few scenarios to illustrate how git hooks are used.

Set up the Software warehouse

First, create a new empty repository under the user directory, named proj .

mkdir ~/projcd ~/projgit initInitialized empty Git repository in /home/demo/proj/.git/

We are now in this git-controlled directory and there is nothing in the directory. Before adding anything, let's go into .git this hidden directory:

cd .gitls -Fbranches/  config  description  HEAD  hooks/  info/  objects/  refs/

Here you can see some files and directories. We are interested in hooks this directory:

cd hooksls -ltotal 40-rwxrwxr-x 1 demo demo  452 Aug  8 16:50 applypatch-msg.sample-rwxrwxr-x 1 demo demo  896 Aug  8 16:50 commit-msg.sample-rwxrwxr-x 1 demo demo  189 Aug  8 16:50 post-update.sample-rwxrwxr-x 1 demo demo  398 Aug  8 16:50 pre-applypatch.sample-rwxrwxr-x 1 demo demo 1642 Aug  8 16:50 pre-commit.sample-rwxrwxr-x 1 demo demo 1239 Aug  8 16:50 prepare-commit-msg.sample-rwxrwxr-x 1 demo demo 1352 Aug  8 16:50 pre-push.sample-rwxrwxr-x 1 demo demo 4898 Aug  8 16:50 pre-rebase.sample-rwxrwxr-x 1 demo demo 3611 Aug  8 16:50 update.sample

There are already some things in this. The first thing you can see is that every file in the directory is marked as "executable." Scripts are called by file names, so they must be executable, and the first line of their content must have a shebang magic number (#!) referenced to the correct script parser. Common scripting languages include bash, Perl, Python, and so on.

Second, we can see that all the files now have a .sample suffix name. git determines whether a hook file is executed entirely by its file name, which is .sample not executed, so if you want to activate a hook, you need to remove the suffix name.

Now, go back to the project root directory:

cd ../..
Model 1: Deploy code on a local Web server with a "post-commit" class hook

The first demonstration will use a post-commit hook from the local Web server to commit the code. We'll have git do a deployment once every commit commits--which of course doesn't work for the production environment, but you know what that means.

First install an Apache:

sudo apt-get updatesudo apt-get install apache2

Our script needs to be able to modify the /var/www/html content under the path (Web server root), so we need to add write permissions. We can set the current system user directly to the owner of the directory:

sudo chown -R `whoami`:`id -gn` /var/www/html

Next, go back to our project directory and create a index.html file:

cd ~/projnano index.html

Write something inside:

Save the exit and tell Git to trace the file:

git add .

Now, we're going to start setting up the hook for this warehouse post-commit . .git/hookscreate this file in the directory:

vim .git/hooks/post-commit

Before writing this file, let's look at how git sets up the environment when it runs the hook.

About the environment variables for git hooks

Some environment variables are involved when the hook is called. To get our script to work, we need to change post-commit the environment variables that git has changed when it calls the hook.

This is a particular point to note when writing git hooks. GIT sets different environment variables when it calls different hooks. In other words, different hooks can cause git to pull information from different environments.

As a result, your scripting environment becomes out of control, and you may not even realize which variables are automatically changed. Unfortunately, these changed variables are not fully documented in the GIT documentation.

Fortunately, Mark Longair found a test method to check the environment variables that were changed when each hook was called. This test method only requires you to paste the following lines of code into your git hook script:

#!/bin/bashecho Running $BASH_SOURCEset | egrep GITecho PWD is $PWD

He wrote this article in 2011, when the git version was in 1.7.1. I wrote this article in the August 2014, the git version is 1.9.1, the operating system is Ubuntu 14.04, it should be said there are some changes. In summary, here are my test results:

In the following tests, the local project directory is /home/demo/test_hooks , and the remote path is /home/demo/origin/test_hooks.git .

  • Hooks : applypatch-msg , pre-applypatch , post-applypatch

    • Environment Variables :
    • git_author_date= ' Mon, 11:25:16-0400 '

    • [email protected]

    • Git_author_name= ' Demo User '

    • Git_internal_gettext_sh_scheme=gnu

    • Git_reflog_action=am

    • working directory :/home/demo/test_hooks

  • Hooks:,, pre-commit prepare-commit-msg commit-msg ,post-commit

    • Environment Variables :
    • Git_author_date= ' @1407774159-0400 '

    • [Email protected]

    • Git_author_name= ' Demo User '

    • Git_dir=.git

    • Git_editor=:

    • Git_index_file=.git/index

    • git_prefix=

    • working directory :/home/demo/test_hooks

  • Hooks:pre-rebase

    • Environment Variables :
    • Git_internal_gettext_sh_scheme=gnu

    • Git_reflog_action=rebase

    • working directory :/home/demo/test_hooks

  • Hooks:post-checkout

    • Environment Variables :
    • Git_dir=.git

    • git_prefix=

    • working directory :/home/demo/test_hooks

  • Hooks:post-merge

    • Environment Variables :
    • Githead_4b407c ...

    • Git_dir=.git

    • Git_internal_gettext_sh_scheme=gnu

    • git_prefix=

    • Git_reflog_action= ' pull Other master '

    • working directory :/home/demo/test_hooks

  • Hooks:pre-push

    • Environment Variables :
    • git_prefix=

    • working directory :/home/demo/test_hooks

  • Hooks:,, pre-receive update post-receive ,post-update

    • Environment Variables :
    • Git_dir=.

    • working directory :/home/demo/origin/test_hooks.git

  • Hooks:pre-auto-gc

    • This is hard to test, so information is missing
  • Hooks:post-rewrite

    • Environment Variables :
    • Git_author_date= ' @1407773551-0400 '

    • [Email protected]

    • Git_author_name= ' Demo User '

    • Git_dir=.git

    • git_prefix=

    • working directory :/home/demo/test_hooks

These are the environments that git sees when it calls different hooks. With this information, we can go back and write our script.

Go back and write the script.

We now know the post-commit environment variables that the hook will change. Write down this information.

Git hooks is a standard script, so tell git what interpreter to use in the first line:

#!/bin/bash

Then we'll have git unpack the latest version of the Code repository (after the latest commit) to the Web server's root directory. This requires that the working directory be set to the Apache file root directory, and the Git directory is set as the repository directory.

At the same time, we need to make sure that this process succeeds every time, even if there is a conflict to enforce. The following script is written in this way:

#!/bin/bashgit --work-tree=/var/www/html --git-dir=/home/demo/proj/.git checkout -f

At the this point, we is almost done. However, we need to look for extra close at the environmental variables, which is set each time the post-commit hook is called. In particular, the are GIT_INDEX_FILE set to .git/index .

This is basically done. The next task is to work on environment variables. post-commitOne of the environment variables changed when the hook was called is GIT_INDEX_FILE changed .git/index , and this is the focus of our attention.

This path is relative to the working path, and our current working path is /var/www/html , and here is no .git/index directory, causing the script to go wrong. So, we need to manually change this variable back to the correct path. This unset instruction needs to be placed before the checkout directive, like this:

#!/bin/bashunset GIT_INDEX_FILEgit --work-tree=/var/www/html --git-dir=/home/demo/proj/.git checkout -f

Many times, this kind of problem is difficult to follow. If you don't realize the problem of environment variables before using git hooks, you'll always be digging around.

In short, our script is complete and now save the exit.

Then, we need to add execute permissions to this script file:

chmod +x .git/hooks/post-commit

Now go back to the directory where the project is, and try a commit.

cd ~/projgit commit -m "here we go..."

Now look at the effect in the browser, is not what we just wrote index.html :

http://你的服务器IP

As we can see, the code you just submitted has been automatically deployed to the Web server's file root directory. Try again to update the point content:

echo "<p>Here is a change.</p>" >> index.htmlgit add .git commit -m "First change"

Refresh the browser page to see if the change takes effect:

You see, this makes local testing much more convenient. Of course, as we said earlier, the production environment is not such a use. The code to be on the production environment must be carefully tested and validated.

Deploy to another production server using a git hook

Below I will demonstrate the correct posture for deploying code on a production environment server. I will use the Push-to-deploy model to trigger code updates for online Web servers when we push code to a bare git repository (bare git repo).

The machine we just had is now used as a development machine, and we will deploy it automatically after each commit, so we can see the change effect at any time.

Next, I'll set up another server to do our production server. There is a bare repository on this server to receive push, and a git hook that can be triggered by push behavior. Then, perform the following steps with the normal user under sudo permissions.

Set up the post-receive hook for the production server

First, install the Web server on the production server:

sudo apt-get updatesudo apt-get install apache2

Don't forget to set permissions on Git:

sudo chown -R `whoami`:`id -gn` /var/www/html

And don't forget to install Git:

sudo apt-get install git

Then, you create a project directory of the same name under the user's home directory. Then, initialize a bare repository under this directory. The bare repository is not a working path, and it is more suitable for servers that do not operate directly on a regular basis.

mkdir ~/projcd ~/projgit init --bare

Because this is a bare repository, it does not have a working path, and .git all files under the path of a normal Git repository will appear directly in the root directory of the bare repository.

Now, create our post-receive hook, which is triggered when the server receives the hook git push . To open this file with an editor:

nano hooks/post-receive

The first line is still to define our script type. Then, tell git what we want to do, or, just like we post-commit did before, unpack the file to the file root of the Web server:

#!/bin/bashgit --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f

Because it is a bare repository, --git-dir you need to specify an absolute path. The others are the same.

Then we need to add some extra logic because we don't want to deploy the branch code labeled as a test-feature production server. Our production server deploys only master the content of the branch.

As you can see in the previous table, post-receive hooks can accept three of the content written to the script via standard input from git, including the last version of the commit hash (), the latest version of the Commit hash (), and the reference name. We can use this information to check if ref is a master branch.

First we need to read the content from the standard input. When each ref is pushed, the above three messages are provided to the script in the format of the standard input, and three messages are separated by a space. We can read this while information in a loop and put the git command above into this loop:

#!/bin/bashwhile read oldrev newrev refdo    git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -fdone

Then we need to add a decision condition. A push from the master branch, whose ref usually contains a refs/heads/master field. This can be used as a basis for our determination:

#!/bin/bashwhile read oldrev newrev refdo    if [[ $ref =~ .*/master$ ]];    then        git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f    fidone

On the other hand, server-side hooks allow Git to pass some messages back to the client. The content sent to the standard output is forwarded to the client, and we can use this feature to send notifications to the user.

This notification should contain a description of the scenario and what the system eventually performed. For push from non-master, we should also return information to the user and tell them why the push was successful but the code was not deployed to the line:

#!/bin/bashwhile read oldrev newrev refdo    if [[ $ref =~ .*/master$ ]];    then        echo "Master ref received.  Deploying master branch to production..."        git --work-tree=/var/www/html --git-dir=/home/demo/proj checkout -f    else        echo "Ref $ref successfully received.  Doing nothing: only the master branch may be deployed on this server."    fidone

When you are finished editing, save the exit.

Finally, don't forget to set the script file to executable:

chmod +x hooks/post-receive

Now, we can access this remote server on our client.

Configure the remote server on the client

Now go back to our client, the development machine, into the project directory:

cd ~/proj

We are going to add our remote server in this directory, called production . You need to know the user name on the remote server, the IP or domain name of the server, and the path of the bare repository relative to the user's home directory. The entire operating instruction looks like this:

git remote add production [email protected]_domain_or_IP:proj

To push a look at:

git push production master

If your SSH key is not set yet, you will need to have your password typed in. The content returned by the server should look like this:

Counting objects: 8, done.Delta compression using up to 2 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (4/4), 473 bytes | 0 bytes/s, done.Total 4 (delta 0), reused 0 (delta 0)remote: Master ref received.  Deploying master branch...To [email protected]:proj   009183f..f1b9027  master -> master

We can see the information we just post-receive wrote in the hook here. If we access the IP or domain name of the remote server from the browser, we should be able to see the latest version of the page:

It seems that this hook has successfully deployed our code to the production environment.

Now continue to test. We create a new branch on the development machine test_feature , check it in to the branch below:

git checkout -b test_feature

Now, the changes we make will be test_feature made in this Test branch. To pity Dorado things first:

echo "

After this commit, enter the developer's IP in the browser and you should be able to see the change:

As we need, the Web server content on the development machine is updated. This is convenient for local testing.

Then, try test_feature pushing this to the remote server:

git push production test_feature

The post-receive results returned from the hook should look like this:

Counting objects: 5, done.Delta compression using up to 2 threads.Compressing objects: 100% (2/2), done.Writing objects: 100% (3/3), 301 bytes | 0 bytes/s, done.Total 3 (delta 1), reused 0 (delta 0)remote: Ref refs/heads/test_feature successfully received. Doing nothing: only the master branch may be deployed on this serverTo [email protected]:proj 83e9dc4..5617b50 test_feature -> test_feature

Enter the IP address of the production server in the browser and there should be no change. This is exactly what we need because our changes were not submitted to master.

Now, if we finish the test and want to push the change to the production server, we can do that. First, check in to the master branch and merge the test_feature branches just now:

git checkout mastergit merge test_feature

Once the merge is complete, push to the production server:

git push production master

Now go to the browser to enter the IP of the production server to see, the changes were successfully deployed:

This workflow, implemented in real-time deployment on the development machine, achieved the push master deployment in the production environment, and was happy.

Summarize

At this point, you should have a general idea of how git hooks is used, and how you can use it to automate your tasks. It can be used to deploy code that can be used to maintain code quality and reject any changes that do not conform to the requirements.

Although Git hooks is easy to use, it is often not easy to master, and the process of troubleshooting after a problem is annoying. In order to write efficient hooks, long-term practice is required to play clear on various configurations, parameters, standard inputs, and environment variables. This can take a long time, but these inputs will ultimately help you and your team to avoid a lot of manual work, resulting in higher returns.

This article originates from Digitalocean Community. English Original: How to use Git Hooks to automate development and Deployment Tasks by Justin Ellingwood

Translation: Lazycai

Automating development deployment tasks with Git hooks

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.