Skip to content

Commit

Permalink
update message to include more git info
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Devyatkin <[email protected]>
  • Loading branch information
Andrey9kin committed Sep 10, 2021
1 parent 10ab34c commit 3e35ec9
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 46 deletions.
47 changes: 47 additions & 0 deletions helpers_git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import subprocess

def resolve_git_ref_to_sha1(ref_name):
print(f'Resolving {ref_name} to Git SHA1...')
sha1 = subprocess.getoutput(f'git rev-parse {ref_name}')
print(f'{ref_name} = {sha1}')
return sha1


def get_commit_message_for_ref(ref_name):
print(f'Getting commit message for {ref_name}...')
msg = subprocess.getoutput(f'git log -n1 --pretty=tformat:%s%b {ref_name}')
print(f'Message = {msg}')
return msg


def get_author_email_for_ref(ref_name):
print(f'Getting author email for {ref_name}...')
email = subprocess.getoutput(f'git --no-pager show -s --format=%ae {ref_name}')
print(f'Author email = {email}')
return email


def get_committer_email_for_ref(ref_name):
print(f'Getting committer email for {ref_name}...')
email = subprocess.getoutput(f'git --no-pager show -s --format=%ce {ref_name}')
print(f'Committer email = {email}')
return email


def generate_diff(base_branch, current_commit_id, repo_url=''):
remote_name = subprocess.getoutput('git remote')
if remote_name == '':
raise Exception(f'Can not get remote name. Out put of git remote command is: {remote_name}')
base_sha1 = resolve_git_ref_to_sha1(f'{remote_name}/{base_branch}')
diff = f'{base_sha1}..{current_commit_id}'

# Add a list of commits that you are about to promote
cmd = f'git log --pretty=format:"%h %<(27)%ai %<(20)%an %s" --graph {diff}'
diff_info = f'Change log for changes to approve:\n```\n{cmd}\n'
diff_info += subprocess.getoutput(cmd)
diff_info += '\n```\n\n'

if 'github' in repo_url or 'gitlab' in repo_url:
diff_info += f'\nFull diff for changes to approve: {repo_url}/compare/{diff}\n\n'

return diff_info
44 changes: 44 additions & 0 deletions helpers_slack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os

from slack_bolt import App
from slack_sdk.errors import SlackApiError

def init_app(slack_bot_token, approve_action_id, cancel_action_id):
app = App(token=slack_bot_token)

@app.action(approve_action_id)
def approve_request(ack, respond, body):
# Acknowledge action request
ack()
print(body)
username = body['user']['username']
original_text = body['message']['blocks'][0]['text']['text']
respond(original_text + f'\n\nApproved by {username}')
os._exit(0)

@app.action(cancel_action_id)
def approve_request(ack, respond, body):
# Acknowledge action request
ack()
print(body)
username = body['user']['username']
original_text = body['message']['blocks'][0]['text']['text']
respond(original_text + f'\n\nCanceled by {username}')
os._exit(1)

@app.middleware
def middleware_func(logger, body, next):
logger.info(f"request body: {body}")
next()

return app

def user_id_by_email(app, email):
try:
result = app.client.users_lookupByEmail(email=email)
return result['user']['id']
except SlackApiError as err:
if err.response['error'] == 'users_not_found':
return None

return None
33 changes: 33 additions & 0 deletions helpers_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytz
from datetime import datetime

def is_business_hours(timezone):
tz = pytz.timezone(timezone)
now = datetime.now(tz)
return True if now.hour > 8 and now.hour < 19 else False


def is_friday_evening(timezone):
tz = pytz.timezone(timezone)
now = datetime.now(tz)
return True if now.isoweekday() == 5 and now.hour > 14 else False


def current_time(timezone):
tz = pytz.timezone(timezone)
now = datetime.now(tz)
return "{}:{}:{}".format(now.hour, now.minute, now.second)


def generate_time_based_message(prod_branch, branches, timezone):
if prod_branch not in ' '.join(branches):
return ''
message = f'\nIt is {current_time()} in {timezone}.'
if is_friday_evening():
return (message
+ ' Deploying to production during Friday afternoon hours?'
+ ' *This a sure way to screw up your evening and potentialy weekend!*\n'
+ ' Make sure you are around to deal with consecuences')
if is_business_hours():
return message + ' *Business hours - think twice before deploying to production!*\n'
return message + ' A good time to attempt deploy\n'
101 changes: 56 additions & 45 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,70 @@
import os
import logging
from time import sleep
import uuid
import helpers_slack
import helpers_git
import helpers_time
from time import sleep, timezone

from slack_bolt import App, logger
from slack_bolt.adapter.socket_mode import SocketModeHandler
from slack_sdk.web import client

# Install the Slack app and get xoxb- token in advance
app = App(token=os.environ["SLACK_BOT_TOKEN"])

# ID of channel you want to post message to
channel_name = "magic-button-test" #TODO: os.environ["SLACK_CHANNEL_NAME"]
if __name__ == "__main__":

# GitHub Project Name
github_project_name = "test-api" #TODO: os.environ["GIT_REPO_NAME"]
# Get job parameters
build_job_name = os.environ['BUILD_JOB_NAME']
build_job_url = os.environ['BUILD_JOB_URL']
current_commit_id = os.environ['CURRENT_GIT_COMMIT']
repo_name = os.environ['REPOSITORY_NAME']
repo_url = os.environ['REPOSITORY_URL']
slack_channel_name = os.environ['SLACK_CHANNEL_NAME']
branches_to_promote = os.environ['BRANCHES_TO_PROMOTE'].split()
timeout_minutes = int(os.environ['TIMEOUT_MINUTES'])
timezone = os.environ['TIMEZONE']
production_branch = os.environ['PRODUCTION_BRANCH']
slack_bot_token = os.environ["SLACK_BOT_TOKEN"]

@app.action(github_project_name + "_yes")
def approve_request(ack, say):
# Acknowledge action request
ack()
say("Great! I will do! Request approved 👍 !")
#TODO: Replase existing message and remove buttons for prevert second click
os._exit(0)
ok_id = uuid.uuid4().hex
nok_id = uuid.uuid4().hex
app = helpers_slack.init_app(slack_bot_token, ok_id, nok_id)

@app.action(github_project_name + "_no")
def approve_request(ack, say):
# Acknowledge action request
ack()
say("Up to you! Request NOT approved :thumbsdown: !")
#TODO: Replase existing message and remove buttons for prevert second click
os._exit(1)
committer_email = helpers_git.get_committer_email_for_ref(current_commit_id)
committer_slack_id = helpers_slack.user_id_by_email(app, committer_email)
commiter_id = committer_slack_id if committer_slack_id is not None else committer_email
author_email = helpers_git.get_author_email_for_ref(current_commit_id)
author_slack_id = helpers_slack.user_id_by_email(app, author_email)
author_id = author_slack_id if author_slack_id is not None else author_email
commit_msg = helpers_git.get_commit_message_for_ref(current_commit_id)

text_for_request = f'Job `{build_job_name}` requires approval to proceed.\n'
text_for_request += 'If approved will promote commit(s) below to branch '
text_for_request += ' and '.join(f'`{branch}`' for branch in branches_to_promote)
text_for_request += f' in repository `{repo_name}`'
text_for_request += f'\nJob will be autocanceled in {timeout_minutes} minutes if no action taken.\n\n'
text_for_request += 'Details:\n'
text_for_request += f'Job URL: {build_job_url}\n'
text_for_request += f'Commit message: `{commit_msg}`; commit id `{current_commit_id}`\n\n'
text_for_request += f'Committer: <@{commiter_id}>\n'
text_for_request += f'Author: <@{author_id}>\n\n'

@app.middleware
def middleware_func(logger, body, next):
logger.info(f"request body: {body}")
next()
for branch in branches_to_promote:
text_for_request += helpers_git.generate_diff(branch, current_commit_id, repo_url)
text_for_request += helpers_time.generate_time_based_message(production_branch, branches_to_promote, timezone)

# Truncate too long messages to prevent Slack from posting them as several messages
# We saw Slack splitting message into two after 3800 but haven't found any documentation
# So number is more or less made up and needs further verification.
if len(text_for_request) > 3500:
text_for_request = text_for_request[:3500]
text_for_request += '\ntoo long message - the rest was truncated. Use link above to see full diff'

if __name__ == "__main__":
# message = app.client.chat_postMessage(
# channel=channel_id,
# text="Test Message"
# )
# print(message)
# logging.info(f'Message resp: {message}')
# SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"], trace_enabled=True).connect()

blocks_json = [
{
"type": "section",
"text": {
"type": "plain_text",
"text": f'Do you want to deploy project {github_project_name}? I will wait 30s',
"emoji": True
"type": "mrkdwn",
"text": text_for_request
}
},
{
Expand All @@ -63,12 +74,12 @@ def middleware_func(logger, body, next):
"type": "button",
"text": {
"type": "plain_text",
"text": "Yes",
"text": "Approve",
"emoji": True
},
"value": "click_me_123",
"style": "primary",
"action_id": github_project_name + "_yes",
"action_id": ok_id,
"confirm": {
"title": {
"type": "plain_text",
Expand All @@ -92,21 +103,21 @@ def middleware_func(logger, body, next):
"type": "button",
"text": {
"type": "plain_text",
"text": "No",
"text": "Cancel",
"emoji": True
},
"value": "click_me_123",
"style": "danger",
"action_id": github_project_name + "_no"
"action_id": nok_id
}
]
}
]

message_deploy = app.client.chat_postMessage(
channel=channel_name,
channel=slack_channel_name,
text="My Text",
blocks=blocks_json
)
sleep(30) # Time in seconds
sleep(timeout_minutes*60) # Time in seconds
SocketModeHandler(app).close()
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
slack_bolt == 1.9.0
slack_bolt == 1.9.0
pytz==2021.1
14 changes: 14 additions & 0 deletions test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

export BUILD_JOB_NAME=local-test
export BUILD_JOB_URL=http://whatever.com
export CURRENT_GIT_COMMIT=$(git rev-parse HEAD)
export REPOSITORY_NAME=$(basename $(git rev-parse --show-toplevel))
export REPOSITORY_URL=https://github.com/fivexl/magic-button
export BRANCHES_TO_PROMOTE=test
export TIMEOUT_MINUTES=10
export TIMEZONE=$(cat /etc/timezone)
export PRODUCTION_BRANCH=release
export SLACK_CHANNEL_NAME=magic-button-test

python3 main.py

0 comments on commit 3e35ec9

Please sign in to comment.