In our project, with every delay, developers, testers and a couple more people get wonderful letters:
Subject: Our draft version v1.1.1 has been updated on the 'testing' server
user1 posted the following updates to the 'testing' server:
Task commits:
jira.local / browse / PROJECT-1234
')
The full list of commits from the previous update:
4392a53 Thu Aug 18 17:50:32 2011 +0700 user1 / [PROJECT-1234] did the useful
f2fcfe2 Thu Aug 18 17:37:53 2011 +0700 user1 / did terrible
cb1fcbe Wed Aug 17 15:18:10 2011 +0700 user2 / registered
File Changes:
file1 | 4 ++ -
file2 | 8 ++++ ----
file3 | 8 ++++ ----
3 files changed, 10 insertions (+), 10 deletions (-)
This solution helped us to get rid of tester questions “Well, have you already posted the fix XXX bug?”, “What's new on the test server?”. Also, all team members, implementation department and management are aware of what happens with the code on the servers.
It uses git, capistrano (+ multistage), php, bash (+ some console utilities). If interested - go under the cat.
Work algorithm
- We update the code on the testing server (cap testing deploy)
- After deploy: restart, a hook is created that creates a tag in the repository. The tag is generated based on the project version (stored in the config, in the repository), the name of the staging server and the name of the release
- A hook is triggered in the repository. If a non-tag arrived - ignore, if the same tag:
- We cut it into components: version, server
- We define the previous tag on the same server
- If there is no tag, then this first installation means and it’s not worth generating differences, there may be several thousand commits
- If there is a tag, we generate a list of differences, pull out a list of tasks from them; compile a list of modified files
- Sending generated email
Tag creation
About the setting of capistrano and capistrano-multistage already where it is not written, so I will only tell you how we add the tag.
We assume that we have a
configs.ini file in the root of the repository, which contains the key
runtime.version . The basis was taken
gist # 381852 .
namespace :deploy do ... after "deploy:restart", "deploy:git:push_deploy_tag" namespace :git do desc "Place release tag into Git and push it to server." task :push_deploy_tag do user = `git config --get user.name`.strip email = `git config --get user.email`.strip version = `git cat-file -p #{real_revision}:configs.ini | fgrep runtime.version | awk -F '[ =]+' '{print $2}'`.strip puts `git tag v#{version}-#{stage}-#{release_name} #{real_revision} -m "Deployed by #{user} <#{email}>"` puts `git push --tags` end end end
What's going on here:
- We retrieve the data of the current user (name and mail) from the config file
- We take the configs.ini file from the installed revision and pull out the version
- Create an annotated tag. In the annotation indicate who and when zadeploil
- Publish tags
We process repository update
The
pre-receive hook (stdin) serves 3 values: previous and current revisions, refname.
We read the incoming parameters and make sure that the tag came:
while read oldrev newrev refname do rev_type=$(git cat-file -t $newrev 2>/dev/null) case "$refname","$rev_type" in refs/tags/*,tag) ... ;; esac done
Select the name of the tag, break it into parts, look for the previous tag for this server:
tag=${refname##refs/tags/} version=`echo $tag | cut -d- -f1` server=`echo $tag | cut -d- -f2` prevtag=$(git describe --tags --abbrev=0 --match="*-$server-*" $newrev^ 2>/dev/null)
If $ prevtag is empty, then this is the first installation on the server. If the version of the new and old tags match - this is an update, if not - installing a new version. Thus, we generate a valid message header.
Let's start to form the body of the letter. First - let's define who dared to fool:
eval $(git for-each-ref --shell --format=' tagger=%(taggername) tagged=%(taggerdate)' $refname ) echo "$tagger '$server':" > msg
Now let's sort the commits by task. The last ones in Jira are named after the mask <project alias> - <task id>, all developers must specify the task alias (uppercase) in the commit. If a task is larger than 30 minutes and requires more than 1 commit, a branch is created, according to the task alias, and then we do not mention this same task in the commits. So, to get the task list, we need to do some simple processing with regular work:
git log $rev_range --abbrev-commit --pretty="format:%s" > tmpfile php >tickets <<END <?php \$f = file_get_contents("$tmp"); if (preg_match_all("#([AZ.]+-\d+)#", \$f, \$matches)) { \$matches[1] = array_unique(\$matches[1]); foreach (\$matches[1] as \$match) { echo '$JIRA_HOST/browse/', \$match, PHP_EOL; } } END
If, as a result, the
tmpfile file
is not empty, add it to the body of the letter.
Next comes the information that interests only the developers of the project: lists of commits and changed files:
echo " :" >> msg git log $rev_range --no-merges --abbrev-commit --pretty="format:%h %ad %an / %s" >> msg echo -e "\n\n :" >>msg git diff --stat=140,110 $rev_range >>msg
And finally, stupid sending letters:
cat msg | mail -s "$subject" $MAIL_TO
All files can be taken on github:
github.com/zvirusz/git-deploy-notifyPS If someone helps to rewrite a piece of code pulling the names of the tasks in perl / bash - I will be very happy.