Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ Qualcomm PR email addresses checker
For each commit in a PR, validates that the commit's author and committer email
addresses are appropriate for the repo.

**NOTE:** This action should be used with `pull_request` or
`pull_request_target` events. It exits successfully without checking anything
for other event types during normal execution. The `--test` option intentionally
uses fixture data regardless of the event type.
**NOTE:** This action should be used with `pull_request`,
`pull_request_target`, or `workflow_run` events. For `workflow_run` events, it
checks all pull requests associated with a workflow whose triggering event is a
pull request event. It exits successfully without checking anything for other
event types during normal execution. The `--test` option intentionally uses
fixture data regardless of the event type.

## Example Usage

Expand Down
1 change: 1 addition & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ runs:
GITHUB_TOKEN: ${{ github.token }}
PULL_NUMBER: ${{ github.event.number }}
COMMITS_COUNT: ${{ github.event.pull_request.commits }}
WORKFLOW_RUN_EVENT: ${{ github.event.workflow_run.event }}
101 changes: 85 additions & 16 deletions check_email_pr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ MYDIR=$(dirname -- "$MYPROG")
debug() { echo "::debug::$1" >&2 ; } # message
error() { echo "::error::$1" >&2 ; } # message

# Make an authenticated request to the GitHub REST API.
github_api_get() {
local endpoint=$1
debug "Getting data from $endpoint"
local debug_opts=()
[ -n "$RUNNER_DEBUG" ] && debug_opts=("--verbose" "--progress-meter" "--show-error")
curl -L --no-progress-meter --fail-with-body \
"${debug_opts[@]}" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$endpoint"
}

# https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request
get_pr_metadata() {
local endpoint="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/$PULL_NUMBER"
local metadata
metadata=$(github_api_get "$endpoint") || return 1
COMMITS_COUNT=$(jq -r '.commits // empty' <<< "$metadata")
if [ -z "$COMMITS_COUNT" ] ; then
error "Cannot determine commit count for pull request $PULL_NUMBER"
return 1
fi
}

# https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-commits-on-a-pull-request
get_pr_commits() {
if [ -n "$TEST_MODE" ] ; then
Expand All @@ -20,15 +46,7 @@ get_pr_commits() {
# TODO: Handle paginated results
# https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28#using-link-headers
local endpoint="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/$PULL_NUMBER/commits?per_page=$COMMITS_COUNT"
debug "Getting commits from $endpoint"
local debug_opts=()
[ -n "$RUNNER_DEBUG" ] && debug_opts=("--verbose" "--progress-meter" "--show-error")
curl -L --no-progress-meter --fail-with-body \
"${debug_opts[@]}" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$endpoint"
github_api_get "$endpoint"
}

# https://docs.github.com/en/rest/repos/custom-properties?apiVersion=2022-11-28#get-all-custom-property-values-for-a-repository
Expand Down Expand Up @@ -69,10 +87,36 @@ EOF
is_pr_event() {
case "${GITHUB_EVENT_NAME:-}" in
pull_request|pull_request_target) return 0 ;;
workflow_run)
case "${WORKFLOW_RUN_EVENT:-}" in
pull_request|pull_request_target) return 0 ;;
esac
return 1
;;
*) return 1 ;;
esac
}

get_pr_numbers() {
if [ "${GITHUB_EVENT_NAME:-}" = "workflow_run" ] ; then
jq -r '.workflow_run.pull_requests[]?.number // empty' "$GITHUB_EVENT_PATH"
else
echo "$PULL_NUMBER"
fi
}

check_pr_commits() {
local result=0
while read -r pr_commit ; do
debug "Running check on: $pr_commit"
"$MYDIR"/src/check_email.sh \
--commit-json "$pr_commit" \
--custom-properties-json "$custom_properties" \
"${TEST_MODE[@]}" || result=1
done < <(get_pr_commits | split_commits_and_add_metadata)
return "$result"
}

TEST_MODE=()
while [ $# -gt 0 ] ; do
case "$1" in
Expand All @@ -87,14 +131,39 @@ if [ "${#TEST_MODE[@]}" -eq 0 ] && ! is_pr_event ; then
exit 0
fi

if [ "${#TEST_MODE[@]}" -gt 0 ] ; then
custom_properties=$(get_custom_properties)
check_pr_commits
exit $?
fi

if [ "${GITHUB_EVENT_NAME:-}" = "workflow_run" ] && \
{ [ -z "${GITHUB_EVENT_PATH:-}" ] || [ ! -r "$GITHUB_EVENT_PATH" ]; } ; then
error "Cannot read workflow_run event payload"
exit 1
fi

PR_NUMBERS=$(get_pr_numbers) || {
error "Cannot determine pull requests from workflow_run event payload"
exit 1
}

if [ -z "$PR_NUMBERS" ] ; then
debug "Skipping email check: workflow_run has no associated pull requests"
exit 0
fi

RESULT=0
custom_properties=$(get_custom_properties)
while read -r pr_commit ; do
debug "Running check on: $pr_commit"
"$MYDIR"/src/check_email.sh \
--commit-json "$pr_commit" \
--custom-properties-json "$custom_properties" \
"${TEST_MODE[@]}" || RESULT=1
done < <(get_pr_commits | split_commits_and_add_metadata)
while read -r PULL_NUMBER ; do
[ -n "$PULL_NUMBER" ] || continue
if [ "${GITHUB_EVENT_NAME:-}" = "workflow_run" ] ; then
get_pr_metadata || {
RESULT=1
continue
}
fi
check_pr_commits || RESULT=1
done <<< "$PR_NUMBERS"

exit $RESULT
63 changes: 63 additions & 0 deletions test/check_event_types.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,71 @@ run_pull_request_event_test() {
grep -q 'Running check on:' <<< "$output"
}

run_workflow_run_pull_request_test() {
local event output status
event=$1

if output=$(env -u TEST_MODE \
GITHUB_EVENT_PATH="$ROOT_DIR/test/workflow_run_event.json" \
GITHUB_API_URL=https://api.example.com \
GITHUB_EVENT_NAME=workflow_run \
WORKFLOW_RUN_EVENT="$event" \
PATH="$ROOT_DIR/test/fake_bin/workflow_run:$PATH" \
"$ROOT_DIR/check_email_pr.sh" 2>&1) ; then
status=0
else
status=$?
fi

[ "$status" -eq 0 ]
[ "$(grep -c 'Running check on:' <<< "$output")" -eq 4 ]
}

run_workflow_run_non_pr_event_test() {
local output status

if output=$(env -u TEST_MODE -u PULL_NUMBER -u COMMITS_COUNT \
GITHUB_EVENT_PATH="$ROOT_DIR/test/workflow_run_event.json" \
GITHUB_API_URL= \
GITHUB_EVENT_NAME=workflow_run \
WORKFLOW_RUN_EVENT=push \
PATH="$ROOT_DIR/test/fake_bin:$PATH" \
"$ROOT_DIR/check_email_pr.sh" 2>&1) ; then
status=0
else
status=$?
fi

[ "$status" -eq 0 ]
! grep -q 'curl was called' <<< "$output"
! grep -q 'jq was called' <<< "$output"
}

run_workflow_run_empty_pull_requests_test() {
local output status

if output=$(env -u TEST_MODE -u PULL_NUMBER -u COMMITS_COUNT \
GITHUB_EVENT_PATH="$ROOT_DIR/test/workflow_run_empty_pull_requests_event.json" \
GITHUB_API_URL=https://api.example.com \
GITHUB_EVENT_NAME=workflow_run \
WORKFLOW_RUN_EVENT=pull_request \
PATH="$ROOT_DIR/test/fake_bin/workflow_run:$PATH" \
"$ROOT_DIR/check_email_pr.sh" 2>&1) ; then
status=0
else
status=$?
fi

[ "$status" -eq 0 ]
grep -q 'no associated pull requests' <<< "$output"
}

for event in push workflow_dispatch schedule unset ; do
run_non_pr_event_test "$event"
done
run_pull_request_event_test pull_request
run_pull_request_event_test pull_request_target
run_workflow_run_pull_request_test pull_request
run_workflow_run_pull_request_test pull_request_target
run_workflow_run_non_pr_event_test
run_workflow_run_empty_pull_requests_test
17 changes: 17 additions & 0 deletions test/fake_bin/workflow_run/curl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail

endpoint=${@: -1}

case "$endpoint" in
*/pulls/101|*/pulls/202)
echo '{"commits": 2}'
;;
*/pulls/101/commits\?per_page=2|*/pulls/202/commits\?per_page=2)
cat "$(cd -- "$(dirname -- "$0")/../.." && pwd)/pr_list_commits.json"
;;
*)
echo "Unexpected endpoint: $endpoint" >&2
exit 1
;;
esac
6 changes: 6 additions & 0 deletions test/workflow_run_empty_pull_requests_event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"workflow_run": {
"event": "pull_request",
"pull_requests": []
}
}
31 changes: 31 additions & 0 deletions test/workflow_run_event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"workflow_run": {
"event": "pull_request",
"pull_requests": [
{
"number": 101,
"url": "https://api.github.com/repos/example/example/pulls/101",
"head": {
"ref": "feature-one",
"sha": "1111111111111111111111111111111111111111"
},
"base": {
"ref": "main",
"sha": "2222222222222222222222222222222222222222"
}
},
{
"number": 202,
"url": "https://api.github.com/repos/example/example/pulls/202",
"head": {
"ref": "feature-two",
"sha": "3333333333333333333333333333333333333333"
},
"base": {
"ref": "main",
"sha": "4444444444444444444444444444444444444444"
}
}
]
}
}
Loading