Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1056] Added support for a webhook notification. #1273

Merged
merged 15 commits into from
Apr 29, 2024
33 changes: 33 additions & 0 deletions .scaffold/tests/bats/notify.bats
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,36 @@ load _helper.bash

popd >/dev/null || exit 1
}

@test "Notify: webhook" {
pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1

mock_curl=$(mock_command "curl")

mock_set_output "${mock_curl}" "200" 1
mock_set_output "${mock_curl}" "400" 2

export DREVOPS_NOTIFY_CHANNELS="webhook"
export DREVOPS_NOTIFY_ENVIRONMENT_URL="https://example-environment-notifcation.com"
tannguyen04 marked this conversation as resolved.
Show resolved Hide resolved
export DREVOPS_NOTIFY_WEBHOOK_URL="https://example-webhook-url.com"
export DREVOPS_NOTIFY_WEBHOOK_METHOD="POST"
export DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS='[{"name": "Content-type", "value": "application/json"},{"name": "Authorization", "value": "Bearer API_KEY"}]'
tannguyen04 marked this conversation as resolved.
Show resolved Hide resolved
export DREVOPS_NOTIFY_WEBHOOK_MESSAGE_BODY='{"channel": "XXX", "message": "Hello there"}'
tannguyen04 marked this conversation as resolved.
Show resolved Hide resolved
export DREVOPS_NOTIFY_WEBHOOK_CUSTOM_PARAMETERS_AND_SECRETS='[{"name": "API_KEY", "value": "this-is-api-key"},{"name": "PASSWORD", "value": "this-is-password"}]'
tannguyen04 marked this conversation as resolved.
Show resolved Hide resolved

run ./scripts/drevops/notify.sh
tannguyen04 marked this conversation as resolved.
Show resolved Hide resolved
assert_success

assert_output_contains "Started dispatching notifications."

assert_output_contains "Started Webhook notification."

assert_output_contains "Finished Webhook notification."

assert_output_contains "Finished dispatching notifications."

run ./scripts/drevops/notify.sh
assert_failure

popd >/dev/null || exit 1
}
109 changes: 109 additions & 0 deletions scripts/drevops/notify-webhook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
##
# Notification dispatch to any webhook.
#
# shellcheck disable=SC1090,SC1091

t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && if [ -f ./.env.local ]; then . ./.env.local; fi && set +a && . "${t}" && rm "${t}" && unset t

set -eu
[ "${DREVOPS_DEBUG-}" = "1" ] && set -x

# Deployment environment URL.
DREVOPS_NOTIFY_ENVIRONMENT_URL="${DREVOPS_NOTIFY_ENVIRONMENT_URL:-}"

# Webhook URL.
DREVOPS_NOTIFY_WEBHOOK_URL="${DREVOPS_NOTIFY_WEBHOOK_URL:-}"

# Webhook method like POST, GET, PUT.
DREVOPS_NOTIFY_WEBHOOK_METHOD="${DREVOPS_NOTIFY_WEBHOOK_METHOD:-POST}"

# Webhook custom header as json format.
# Ex: [{"name": "Content-type", "value": "application/json"},{"name": "Authorization", "value": "Bearer API_KEY"}].
DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS="${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS:-}"

# Webhook message body as json format.
# This is data sent to webhook.
# Ex: {"channel": "XXX", "message": "Hello there"}.
DREVOPS_NOTIFY_WEBHOOK_MESSAGE_BODY="${DREVOPS_NOTIFY_WEBHOOK_MESSAGE_BODY:-}"

# The pattern of response code return by curl.
# Default is match 200.
DREVOPS_NOTIFY_WEBHOOK_RESPONSE_CODE_PATTERN="${DREVOPS_NOTIFY_WEBHOOK_RESPONSE_CODE_PATTERN:-^200$}"
# Custom parameters and secrets to use in custom header and message body.
# Ex: [{"name": "API_KEY", "value": "XXX"},{"name": "PASSWORD", "value": "XXX"}]
DREVOPS_NOTIFY_WEBHOOK_CUSTOM_PARAMETERS_AND_SECRETS="${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_PARAMETERS_AND_SECRETS:-}"

# ------------------------------------------------------------------------------

# @formatter:off
note() { printf " %s\n" "${1}"; }
info() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[34m[INFO] %s\033[0m\n" "${1}" || printf "[INFO] %s\n" "${1}"; }
pass() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[32m[ OK ] %s\033[0m\n" "${1}" || printf "[ OK ] %s\n" "${1}"; }
fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; }
# @formatter:on

for cmd in php curl jq; do command -v ${cmd} >/dev/null || {
fail "Command ${cmd} is not available"
exit 1
}; done

Check warning on line 49 in scripts/drevops/notify-webhook.sh

View check run for this annotation

Codecov / codecov/patch

scripts/drevops/notify-webhook.sh#L47-L49

Added lines #L47 - L49 were not covered by tests

[ -z "${DREVOPS_NOTIFY_ENVIRONMENT_URL}" ] && fail "Missing required value for DREVOPS_NOTIFY_ENVIRONMENT_URL" && exit 1
[ -z "${DREVOPS_NOTIFY_WEBHOOK_URL}" ] && fail "Missing required value for DREVOPS_NOTIFY_WEBHOOK_URL" && exit 1
[ -z "${DREVOPS_NOTIFY_WEBHOOK_METHOD}" ] && fail "Missing required value for DREVOPS_NOTIFY_WEBHOOK_METHOD" && exit 1
[ -z "${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS}" ] && fail "Missing required value for DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS" && exit 1
[ -z "${DREVOPS_NOTIFY_WEBHOOK_MESSAGE_BODY}" ] && fail "Missing required value for DREVOPS_NOTIFY_WEBHOOK_MESSAGE_BODY" && exit 1

# Find custom parameters and secrets in string and replace it by value.
# shellcheck disable=SC2001
replace_parameters_and_secrets_in_string() {
string="$1"
while read -r name value; do
string=$(echo "${string}" | sed "s/${name}/${value}/g")
done < <(echo "${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_PARAMETERS_AND_SECRETS}" | jq -r '.[] | "\(.name) \(.value)"')

Check warning on line 63 in scripts/drevops/notify-webhook.sh

View check run for this annotation

Codecov / codecov/patch

scripts/drevops/notify-webhook.sh#L63

Added line #L63 was not covered by tests
tannguyen04 marked this conversation as resolved.
Show resolved Hide resolved

echo "${string}"
}

info "Started Webhook notification."

info "Webhook config:"
note "Environment url : ${DREVOPS_NOTIFY_ENVIRONMENT_URL}"
note "Webhook url : ${DREVOPS_NOTIFY_WEBHOOK_URL}"
note "Webhook method : ${DREVOPS_NOTIFY_WEBHOOK_METHOD}"
note "Webhook custom header :"
echo "${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS}" | jq -c '.[]' | while read -r item; do
name=$(echo "${item}" | jq -r '.name')
value=$(echo "${item}" | jq -r '.value')
note " ${name}: ${value}"
done
note "Webhook custom parameters and secrets :"
echo "${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_PARAMETERS_AND_SECRETS}" | jq -c '.[]' | while read -r item; do
name=$(echo "${item}" | jq -r '.name')
value=$(echo "${item}" | jq -r '.value')
note " ${name}: ${value}"
done

# Build header.
headers_replaced=$(replace_parameters_and_secrets_in_string "${DREVOPS_NOTIFY_WEBHOOK_CUSTOM_HEADERS}")
declare -a headers
while read -r item; do
name=$(echo "${item}" | jq -r '.name')
value=$(echo "${item}" | jq -r '.value')
headers+=("-H" "${name}: ${value}")
done < <(echo "${headers_replaced}" | jq -c '.[]')

# Build message body.
message_body="$(replace_parameters_and_secrets_in_string "${DREVOPS_NOTIFY_WEBHOOK_MESSAGE_BODY}")"

# Make curl request.

if ! curl -L -s -o /dev/null -w '%{http_code}' \
-X "${DREVOPS_NOTIFY_WEBHOOK_METHOD}" \
"${headers[@]}" \
-d "${message_body}" "${DREVOPS_NOTIFY_WEBHOOK_URL}" | grep -q "${DREVOPS_NOTIFY_WEBHOOK_RESPONSE_CODE_PATTERN}"; then
fail "Unable to notify to webhook ${DREVOPS_NOTIFY_WEBHOOK_URL}."
exit 1
fi

pass "Finished Webhook notification."
4 changes: 4 additions & 0 deletions scripts/drevops/notify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,8 @@ if [ -z "${DREVOPS_NOTIFY_CHANNELS##*jira*}" ]; then
./scripts/drevops/notify-jira.sh "$@"
fi

if [ -z "${DREVOPS_NOTIFY_CHANNELS##*webhook*}" ]; then
./scripts/drevops/notify-webhook.sh "$@"
fi

pass "Finished dispatching notifications."
Loading