Last active
December 3, 2020 21:01
-
-
Save josh-stillman/4dd0388c96868669ee7b6e123a9c37e5 to your computer and use it in GitHub Desktop.
GitLab Slackbot Script. Posts daily MRs to Slack to keep environments in sync. Blog here: https://medium.com/giant-machines/slackbot-to-the-rescue-keeping-gitlab-branches-in-sync-with-a-slackbot-ff84f80eaf5f
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Function that will be called for each repo. Script exectuion begins below. | |
handle_repo () { | |
#1. Set up variables. | |
local REPO=$1 # first function argument | |
local PROJECT_ID=$2 # second function argument | |
local SOURCE_BRANCH_DISPLAY=${3:-"QA"} # default value | |
local TARGET_BRANCH_DISPLAY=${4:-"Dev"} # default value | |
# Use exact (lowercase) branch name for Gitlab API calls | |
local SOURCE_BRANCH=$(echo "$SOURCE_BRANCH_DISPLAY" | tr '[:upper:]' '[:lower:]' ) | |
local TARGET_BRANCH=$(echo "$TARGET_BRANCH_DISPLAY" | tr '[:upper:]' '[:lower:]' ) | |
local MR_TITLE="${SOURCE_BRANCH_DISPLAY} => ${TARGET_BRANCH_DISPLAY}" | |
local SLACKBOT_BRANCH="slackbot%2fqa-to-dev" # URL escaped for API calls | |
local SLACKBOT_BRANCH_DISPLAY="slackbot/qa-to-dev" | |
if [ -z "$REPO" ] || [ -z "$SOURCE_BRANCH" ] || [ -z "$TARGET_BRANCH" ] || [ -z "$PROJECT_ID" ]; then | |
echo "Missing environment variables: REPO: $REPO, SOURCE_BRANCH: $SOURCE_BRANCH, TARGET_BRANCH: $TARGET_BRANCH, PROJECT_ID: $PROJECT_ID" | |
return 1 | |
fi | |
echo "Running handle_repo for $REPO: From $SOURCE_BRANCH to $TARGET_BRANCH for project $PROJECT_ID" | |
#2. Delete existing slackbot branch | |
DELETE_BRANCH_RESP=$(curl -X DELETE --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/repository/branches/$SLACKBOT_BRANCH") | |
echo "delete branch resp is $DELETE_BRANCH_RESP" | |
#3. Create remote slackbot branch off of source branch | |
CREATE_BRANCH_RESP=$(curl -X POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/repository/branches?branch=$SLACKBOT_BRANCH&ref=$SOURCE_BRANCH") | |
echo "create branch resp is $CREATE_BRANCH_RESP" | |
#4. Create merge request to target branch | |
CREATE_MR_RESP=$(curl -X POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/merge_requests?source_branch=$SLACKBOT_BRANCH&target_branch=$TARGET_BRANCH&simple=true" --data-urlencode "title=$MR_TITLE") | |
echo "create MR resp is $CREATE_MR_RESP" | |
# Handle Gitlab occasionally retaining yesterday's unmerged MR after delete/create | |
ALREADY_EXISTS=$(echo "$CREATE_MR_RESP" | grep 'already exists') | |
# 5. Get MR ID | |
if [ -n "$ALREADY_EXISTS" ]; then | |
MR_ID="${CREATE_MR_RESP//[^0-9]/}" # extract numbers from response string | |
echo "MR already exists $MR_ID" | |
else | |
MR_ID=$(echo "$CREATE_MR_RESP" | python3 -c "import sys, json; print(json.load(sys.stdin)['iid'])") | |
fi | |
# 6. Get MR info from GitLab API | |
sleep 10 # Wait for GitLab to calculate merge conflicts info | |
MR_INFO_RESP=$(curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/merge_requests/$MR_ID") | |
echo "MR info resp is $MR_INFO_RESP" | |
# 7. Get MR URL | |
URL=$(echo "$MR_INFO_RESP" | python3 -c "import sys, json; print(json.load(sys.stdin)['web_url'])") | |
# 8. Set initial slack message | |
MSG="$REPO: $MR_TITLE: $URL" | |
echo "New MR: $MSG" | |
# 9. Report on merge conflicts | |
CONFLICTS=$(echo "$MR_INFO_RESP" | python3 -c "import sys, json; print(json.load(sys.stdin)['has_conflicts'])") | |
if [ "$CONFLICTS" == 'True' ]; then | |
MSG="$MSG. *Merge Conflicts!* 🙀 _Pull down $SLACKBOT_BRANCH_DISPLAY, merge ${TARGET_BRANCH} into it locally, resolve conflicts, then push back up._ 😸" | |
fi | |
# 10. If MR is empty, close it | |
CHANGES=$(echo "$MR_INFO_RESP" | python3 -c "import sys, json; print(json.load(sys.stdin)['changes_count'])") | |
echo "changes count is $CHANGES" | |
if [ "$CHANGES" == "None" ]; then | |
echo "No changes, closing MR" | |
MSG="$REPO: $SOURCE_BRANCH_DISPLAY is synced with $TARGET_BRANCH_DISPLAY! 🎉" | |
curl -X PUT --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/merge_requests/$MR_ID?state_event=close" | |
fi | |
# 11. Send to Slack | |
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$MSG\"}" "$SLACK_URL" | |
} | |
# Begin script execution here: | |
# Setup env vars. Located in the Gitlab project settings console. For running locally, use a .env file at project root and export the vars. | |
if [ -f "./.env" ]; then | |
source ./.env | |
echo "Importing env vars from .env file" | |
fi | |
if [ -z "$SLACK_URL" ] || [ -z "$GITLAB_TOKEN" ] || [ -z "$REPO_DISPLAY_NAMES" ]; then | |
echo "Missing environment Variables: SLACK_URL: $SLACK_URL, GITLAB_TOKEN: $GITLAB_TOKEN, REPO_DISPLAY_NAMES: $REPO_DISPLAY_NAMES" | |
exit 1 | |
fi | |
# Create array from comma-separated string of repo names | |
IFS=', ' read -r -a REPO_ARRAY <<< "$REPO_DISPLAY_NAMES" | |
# Call function for each repo. For each repo name in the REPO_ARRAY, a matching variable with the GitLab project ID named with the repo name in all caps followed by _ID is required | |
for REPO in "${REPO_ARRAY[@]}"; do | |
REPO_ID="$(echo "$REPO" | tr '[:lower:]' '[:upper:]')_ID" # Capitalize repo display name and add "_ID" to get project id's variable name | |
handle_repo "$REPO" "${!REPO_ID}" # Indirect variable. REPO_ID's value is another variable's name, such as FRONTEND_ID, and we access that second variable's value. | |
# https://stackoverflow.com/questions/16553089/dynamic-variable-names-in-bash | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment