|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Checks local git state for unusual conditions and prompts the user to confirm before deploying. |
| 3 | +# |
| 4 | +# This is a safety measure to prevent accidental deployments of unintended target commits. |
| 5 | +# The purpose is to draw attention to unusual conditions to be reviewed carefully. |
| 6 | +# |
| 7 | +# This script is intended to be used as a pre-deploy hook in a deployment script for manual/local deployments. |
| 8 | +# It is not intended for automated deployments. Set CI=true to skip. |
| 9 | +# |
| 10 | +# Releases typically deploy the HEAD of the release branch. The conditions in this script favor that typical case. |
| 11 | +# There are valid situations (rollback, hotfix, temporary config changes) which trigger warnings. That's OK/expected. |
| 12 | +# In such situations, the user should review carefully and confirm the deployment at their discretion. |
| 13 | +# |
| 14 | +# Usage: $0 <GIT_REMOTE> <GIT_RELEASE_BRANCH> <REVISION> |
| 15 | +# - git_remote: The remote to fetch the release branch from e.g. "origin". |
| 16 | +# - git_release_branch: The release branch to compare the target REVISION against e.g. "main". |
| 17 | +# - revision: The specific commit hash being deployed e.g. "41af8fa0fc80b7f590a0f46eccd4956b4a45c932". |
| 18 | + |
| 19 | +if [ "${CI}" = "true" ]; then |
| 20 | + echo "CI environment detected. Skipping check for unusual local git state." |
| 21 | + exit 0 |
| 22 | +fi |
| 23 | + |
| 24 | +git_remote="$1" |
| 25 | +git_release_branch="$2" |
| 26 | +revision="$3" |
| 27 | + |
| 28 | +if [ -z "${git_remote}" ] || [ -z "${git_release_branch}" ] || [ -z "${revision}" ]; then |
| 29 | + echo "ERROR: Missing required arguments. Usage: $0 <GIT_REMOTE> <GIT_RELEASE_BRANCH> <REVISION>" |
| 30 | + exit 1 |
| 31 | +fi |
| 32 | + |
| 33 | +set -euo pipefail |
| 34 | + |
| 35 | +git fetch "${git_remote}" "${git_release_branch}" || { echo "ERROR: git fetch failed."; exit 1; } |
| 36 | + |
| 37 | +show_warning_prompt='false' |
| 38 | + |
| 39 | +echo "Checking if there are any uncommitted changes..." |
| 40 | +if [ -z "$(git status --porcelain)" ]; then |
| 41 | + echo "(check passed) Git state is clean. There are no uncommitted changes." |
| 42 | +else |
| 43 | + show_warning_prompt='true' |
| 44 | + echo "WARNING: Uncommitted changes detected!" |
| 45 | + echo "⬇ Review the git status. ⬇" |
| 46 | + git status |
| 47 | + echo |
| 48 | +fi |
| 49 | + |
| 50 | +echo "Checking if the currently checked out branch is the release branch ${git_release_branch}..." |
| 51 | +current_branch=$(git rev-parse --abbrev-ref HEAD) |
| 52 | +if [ "${current_branch}" = "${git_release_branch}" ]; then |
| 53 | + echo "(check passed) On ${current_branch} branch." |
| 54 | +else |
| 55 | + show_warning_prompt='true' |
| 56 | + echo "WARNING: Not on ${git_release_branch} branch! Currently on ${current_branch} branch." |
| 57 | + echo "⬇ Review the git log for the currently checked out commit. ⬇" |
| 58 | + git --no-pager log -1 |
| 59 | + echo |
| 60 | +fi |
| 61 | + |
| 62 | +echo "Checking if REVISION=${revision} is the HEAD of the up-to-date remote release branch ${git_remote}/${git_release_branch}..." |
| 63 | +remote_release_branch_head="$(git rev-parse "${git_remote}/${git_release_branch}")" |
| 64 | +if [ "${revision}" = "${remote_release_branch_head}" ]; then |
| 65 | + echo "(check passed) REVISION=${revision} is the HEAD of the up-to-date remote release branch ${git_remote}/${git_release_branch}." |
| 66 | +else |
| 67 | + show_warning_prompt='true' |
| 68 | + |
| 69 | + remote_release_branch_relative_time="$(git --no-pager log -1 --pretty=format:'%cr' "${remote_release_branch_head}")" |
| 70 | + revision_relative_time="$(git --no-pager log -1 --pretty=format:'%cr' "${revision}")" |
| 71 | + |
| 72 | + echo "WARNING: REVISION is not the HEAD of the remote release branch ${git_remote}/${git_release_branch}!" |
| 73 | + echo "- ${remote_release_branch_head} ${git_remote}/${git_release_branch} (${remote_release_branch_relative_time})" |
| 74 | + echo "- ${revision} REVISION (${revision_relative_time})" |
| 75 | + echo "⬇ Review the git log for ${git_remote}/${git_release_branch} (committed ${remote_release_branch_relative_time}). ⬇" |
| 76 | + git --no-pager log -1 "${git_remote}/${git_release_branch}" |
| 77 | + echo "⬇ Review the git log for REVISION (committed ${revision_relative_time}). ⬇" |
| 78 | + git --no-pager log -1 "${revision}" |
| 79 | + echo |
| 80 | +fi |
| 81 | + |
| 82 | +if [ "${show_warning_prompt}" = 'true' ]; then |
| 83 | + printf "WARNING: Unusual conditions detected! Are you sure you want to deploy? (y/n) " |
| 84 | + read -r prompt_answer |
| 85 | + if [ "${prompt_answer}" != "y" ]; then |
| 86 | + echo "Aborting." |
| 87 | + exit 1 |
| 88 | + fi |
| 89 | +fi |
0 commit comments