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

Publish a comment from CircleCI to Github #3173

Merged
merged 1 commit into from
Jul 5, 2021
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: 10 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ orbs:
command: |
tools/circleci-prepare-log-dir.sh
if [ -n "${AWS_SECRET_ACCESS_KEY}" ]; then tools/circleci-upload-to-s3.sh; fi
- run:
name: Publish a comment to GitHub
when: always
command: |
tools/circle-publish-github-comment.sh

package:
parallelism: 1
Expand Down Expand Up @@ -265,6 +270,11 @@ orbs:
command: |
tools/circleci-prepare-log-dir.sh
if [ -n "${AWS_SECRET_ACCESS_KEY}" ]; then tools/circleci-upload-to-s3.sh; fi
- run:
name: Publish a comment to GitHub
when: always
command: |
tools/circle-publish-github-comment.sh

docker_image:
parallelism: 1
Expand Down
228 changes: 228 additions & 0 deletions tools/circle-publish-github-comment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/env bash

source tools/circleci-helpers.sh

set -e

if [ -z "$CIRCLE_BRANCH" ]; then
echo "\$CIRCLE_BRANCH is empty. Do nothing"
exit 0
fi

if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "\$CIRCLE_PULL_REQUEST is empty. Do nothing"
exit 0
fi

if [ "$CIRCLE_PULL_REQUEST" = "false" ]; then
echo "Not a pull request. Do nothing"
exit 0
fi

if [ -z "$COMMENTER_GITHUB_TOKEN" ]; then
echo "\$COMMENTER_GITHUB_TOKEN is empty. Do nothing"
exit 0
fi


# COMMENTER_GITHUB_USER is nickname of a special github user.
# COMMENTER_GITHUB_TOKEN is token with scope public repo.
# Token can be obtained here https://github.com/settings/tokens/new
# Don't use ANY REAL user as a commenter, because GitHub access scopes are too wide.
echo "Used vars (you can use them to debug locally):"
echo "export COMMENTER_GITHUB_TOKEN=fillme"
echo "export COMMENTER_GITHUB_USER=$COMMENTER_GITHUB_USER"
echo "export PRESET=$PRESET"
echo "export CIRCLE_SHA1=$CIRCLE_SHA1"
echo "export CIRCLE_BUILD_URL=$CIRCLE_BUILD_URL"
echo "export CIRCLE_JOB=$CIRCLE_JOB"
echo "export CIRCLE_PROJECT_USERNAME=$CIRCLE_PROJECT_USERNAME"
echo "export CIRCLE_PROJECT_REPONAME=$CIRCLE_PROJECT_REPONAME"
echo "export CIRCLE_PULL_REQUEST=$CIRCLE_PULL_REQUEST"

PR_NUM="${CIRCLE_PULL_REQUEST##*/}"

REPO_SLUG="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"

# IF we only run small tests, the file is missing
touch /tmp/ct_markdown

PRESET="${PRESET:-default}"

function remove_ct_log_links
{
mv /tmp/ct_markdown /tmp/ct_markdown_original
# grep fails if nothing matches
{ grep -v "Report log" /tmp/ct_markdown_original || echo ""; } > /tmp/ct_markdown
}

# https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
function replace_string {
sed -i "s/$( \
echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g' \
| sed -e 's:\t:\\t:g' \
)/$( \
echo "$2" | sed -e 's/[\/&]/\\&/g' \
| sed -e 's:\t:\\t:g' \
)/g" "$3"
}

function rewrite_log_links_to_s3
{
local CT_REPORT=big_tests/ct_report
local CT_REPORT_ABS=$(./tools/abs_dirpath.sh "$CT_REPORT")
local CT_REPORTS=$(ct_reports_dir)
local BIG_TESTS_URL="$(direct_s3_url ${CT_REPORTS})/big"
cp /tmp/ct_markdown /tmp/ct_markdown_original
replace_string "$CT_REPORT_ABS" "$BIG_TESTS_URL" /tmp/ct_markdown
# URL escape for s3_reports.html script
replace_string "ct_run.test@" "ct_run.test%40" /tmp/ct_markdown
}

function last_ct_run_name
{
ls -1 -t big_tests/ct_report/ | grep ct_run | head -n1
}


function small_suite_path
{
if [ -d _build/test/logs ]; then
cd _build/test/logs
ls -t -1 ct_run.mongooseim@localhost.*/lib.mongooseim.logs/run.*/suite.log.html
cd ../../..
fi
}

function ct_run_url
{
local CT_REPORTS=$(ct_reports_dir)
local BIG_TESTS_URL="$(direct_s3_url ${CT_REPORTS})/big"
local RUN_PART=$(echo "$(last_ct_run_name)" | sed "s/@/%40/g")
echo "$BIG_TESTS_URL/$RUN_PART/index.html"
}

function ct_small_url
{
local CT_REPORTS=$(ct_reports_dir)
local SMALL_TESTS_URL="$(direct_s3_url ${CT_REPORTS})/small"
local SUFFIX=$(small_suite_path)
echo "$SMALL_TESTS_URL/$SUFFIX"
}

function reports_url
{
local CT_REPORTS=$(ct_reports_dir)
# Something like that:
# http://esl.github.io/circleci-mim-results/s3_reports.html?prefix=PR/3173/60934/pgsql_mnesia.23.0.3-1
s3_url "${CT_REPORTS}"
}

if [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
REPORTS_URL_BODY="Reports are not uploaded"$'\n'
remove_ct_log_links
else
REPORTS_URL="$(reports_url)"
CT_RUN_URL="$(ct_run_url)"
SMALL_CT_URL="$(ct_small_url)"
REPORTS_BIG_URL_BODY=""
REPORTS_SMALL_URL_BODY=""
if [ -f big_tests/ct_report/index.html ]; then
rewrite_log_links_to_s3
REPORTS_BIG_URL_BODY="/ [big]($CT_RUN_URL)"
fi
if [ -d _build/test/logs ]; then
REPORTS_SMALL_URL_BODY=" / [small]($SMALL_CT_URL)"
fi
REPORTS_URL_BODY="Reports [root](${REPORTS_URL})${REPORTS_BIG_URL_BODY}${REPORTS_SMALL_URL_BODY}"$'\n'
fi

COUNTERS_FILE=/tmp/ct_stats_vars
COUNTERS_BODY=""
if [ -f "$COUNTERS_FILE" ]; then
. "$COUNTERS_FILE"
COUNTERS_BODY="**OK: $CT_COUNTER_OK** "
if [ "$CT_COUNTER_FAILED" != "0" ]; then
COUNTERS_BODY="$COUNTERS_BODY/ **Failed: $CT_COUNTER_FAILED** "
else
COUNTERS_BODY="$COUNTERS_BODY/ Failed: 0 "
fi
if [ "$CT_COUNTER_USER_SKIPPED" != "0" ]; then
COUNTERS_BODY="$COUNTERS_BODY/ **User-skipped: $CT_COUNTER_USER_SKIPPED** "
else
COUNTERS_BODY="$COUNTERS_BODY/ User-skipped: 0 "
fi
if [ "$CT_COUNTER_AUTO_SKIPPED" != "0" ]; then
COUNTERS_BODY="$COUNTERS_BODY/ **Auto-skipped: $CT_COUNTER_AUTO_SKIPPED** "
else
COUNTERS_BODY="$COUNTERS_BODY/ Auto-skipped: 0 "
fi
COUNTERS_BODY="$COUNTERS_BODY"$'\n'
fi

TRUNCATED_BODY=""
# Number of truncated failed tests if file exists
TRUNCATED_FILE="/tmp/ct_markdown_truncated"
if [ -f "$TRUNCATED_FILE" ]; then
TRUNCATED_COUNTER=$(cat "$TRUNCATED_FILE")
TRUNCATED_BODY=$'\n'$'\n'"$TRUNCATED_COUNTER errors were truncated"
fi

# Link to a CircleCI job
JOB_URL="$CIRCLE_BUILD_URL"
DESC_BODY="[$CIRCLE_JOB]($JOB_URL) / $PRESET / $CIRCLE_SHA1"$'\n'
# This file is created by ct_markdown_errors_hook
ERRORS_BODY="$(cat /tmp/ct_markdown || echo '/tmp/ct_markdown missing')"
BODY="${DESC_BODY}${REPORTS_URL_BODY}${COUNTERS_BODY}${ERRORS_BODY}${TRUNCATED_BODY}"

function post_new_comment
{
# Create a comment GitHub API doc
# https://developer.github.com/v3/issues/comments/#create-a-comment
echo "Posting a new comment"
POST_BODY=$(BODY_ENV="$BODY" jq -n '{body: env.BODY_ENV}')
curl -o /dev/null -i \
-H "Authorization: token $COMMENTER_GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-X POST -d "$POST_BODY" \
https://api.github.com/repos/$REPO_SLUG/issues/$PR_NUM/comments
}

function append_comment
{
# Concat old comment text and some extra text
# Keep a line separator between them
# $'\n' is a new line in bash
#
# Edit commment GitHub API doc
# https://developer.github.com/v3/issues/comments/#edit-a-comment
COMMENT_ID="$1"
echo "Patch comment $COMMENT_ID"
BODY_FROM_GH="$(cat /tmp/gh_comment | jq -r .body)"
BODY="${BODY_FROM_GH}"$'\n'$'\n'"---"$'\n'$'\n'"${BODY}"
PATCH_BODY=$(BODY_ENV="${BODY}" jq -n '{body: env.BODY_ENV}')
curl -o /dev/null -i \
-H "Authorization: token $COMMENTER_GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-X PATCH -d "$PATCH_BODY" \
https://api.github.com/repos/$REPO_SLUG/issues/comments/$COMMENT_ID
}

# List comments
# https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
curl -s -S -o /tmp/gh_comments -L \
-H "Authorization: token $COMMENTER_GITHUB_TOKEN" \
-H "Content-Type: application/json" \
https://api.github.com/repos/$REPO_SLUG/issues/$PR_NUM/comments

# Filter out all comments for a particular user
# Then filter out all comments that have a git commit rev in the body text
# Then take first comment (we don't expect more comments anyway)
cat /tmp/gh_comments | jq "map(select(.user.login == \"$COMMENTER_GITHUB_USER\")) | map(select(.body | contains(\"$CIRCLE_SHA1\")))[0]" > /tmp/gh_comment
COMMENT_ID=$(cat /tmp/gh_comment | jq .id)

if [ "$COMMENT_ID" = "null" ]; then
post_new_comment
else
append_comment "$COMMENT_ID"
fi