Prevent accidental deployments on Friday

It is awesome when everything you push to the master branch gets automatically deployed. Usually it works flawlessly. It runs your tests, builds a docker image, and deploys it. The life is great ;)

One Friday afternoon someone wants to go home earlier. That person pushes the code to master. There are some tests and all of them pass. The code can be deployed even though not everything is tested. The code gets deployed. Its author leaves the office. Boom! Something breaks.

The solution is simple. You can quickly revert the changes and carry on. What if this situation happens again? What if it is not just one person but a few people make such mistakes?

One day you decide that the best solution is to avoid deployments on Friday. Can you enforce this rule? Obviously, you can block deployments on Jenkins. What if you need to quickly fix a production bug on Friday? That is allowed, isn’t it?

Maybe we should not push to master on Fridays? We probably should not push to master also when the majority of developers already left the office, so 4 pm deployments are also banned. Looks like a lot of rules to remember.

It will not work. You or someone else will type git push in console without checking the active branch. Even if nothing breaks you can become The One Weirdo Who Deploys On Fridays.

Pre push hook

Fortunately, all you need is a pre-push hook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env python

import sys
import datetime

def remote_branch(stdin_first_line):
"""
Reads the name of the remote git branch from runtime parameters.
In the pre-push.py hook the name of the remote branch is passed as the \$1 parameter.
:param stdin_first_line the first line of the standard input
'master'
'hot-fix'
"""
stdin_parts = stdin_first_line.split(" ")
remote = stdin_parts[2]
remote_parts = remote.split("/")
return remote_parts[-1]

def day_of_week():
"""
Returns the integer indicating the day of week.
Uses the datetime package so, Monday is 0 and Sunday is 6.
"""
return datetime.datetime.today().weekday()

def hour_of_day():
"""
Returns the current hour as an integer.
:return:
"""
return datetime.datetime.today().hour

def should_deploy(branch_name, day_of_week, hour_of_day):
"""
Returns true if the code can be deployed.
:param branch_name: the name of the remote git branch
:param day_of_week: the day of the week as an integer
:param hour_of_day: the current hour
:return: true if the deployment should continue
>>> should_deploy("hot-fix", 0, 10)
True
>>> should_deploy("hot-fix", 4, 10) #this branch can be deployed on Friday
True
>>> should_deploy("hot-fix", 5, 10) #this branch can be deployed on Saturday
True
>>> should_deploy("hot-fix", 6, 10) #this branch can be deployed on Sunday
True
>>> should_deploy("hot-fix", 0, 7) #this branch can be deployed before 8am
True
>>> should_deploy("hot-fix", 0, 16) #this branch can be deployed after 4pm
True
>>> should_deploy("master", 0, 10)
True
>>> should_deploy("master", 4, 10) #master cannot be deployed on Friday
False
>>> should_deploy("master", 5, 10) #master cannot be deployed on Saturday
False
>>> should_deploy("master", 6, 10) #master cannot be deployed on Sunday
False
>>> should_deploy("master", 0, 7) #master cannot be deployed before 8am
False
>>> should_deploy("master", 0, 16) #master cannot be deployed after 4pm
False
"""
if branch_name == "master" and day_of_week >= 4:
return False
elif branch_name == "master" and (hour_of_day < 8 or hour_of_day >= 16):
return False
else:
return True

if __name__ == "__main__":
print "Verifying deployment hours..."

if deploy:
sys.exit(0)
else:
print "[ABORTED] Push stopped by the \"deployment hours\" pre-push hook!"
print "If you know what you are doing and still want to push your code, use the --no-verify parameter"
sys.exit(1)


To install the hook: save the code in pre-push file and make it executable. The file may be copied to repository hooks and run only in one repository. It can also be added to global hooks and run in every repository:

1
2
3
4
mkdir ~/git-global-hooks
cp pre-push.py ~/git-global-hooks/pre-push
chmod u+x ~/git-global-hooks/pre-push


Now you are safe. Every time you try to push to master when you should not do that, you will see this friendly message:

1
2
[ABORTED] Push stopped by the "deployment hours" pre-push hook!
If you know what you are doing and still want to push your code, use the --no-verify parameter


In those rare occasions when you need to deploy on Friday or after 4 pm, just add --no-verify to your git command.

Github repository: https://github.com/mikulskibartosz/pre-push-hook

Remember to share on social media!