From 8948f2b1b778c614753e432a22f67644f5ebb229 Mon Sep 17 00:00:00 2001 From: k1rk <8kirk8@gmail.com> Date: Wed, 16 Aug 2023 12:00:42 +0200 Subject: [PATCH] added support for project parameter added output with comment_id and comment_url --- .github/workflows/release-beta.yaml | 7 +++-- .github/workflows/release.yaml | 14 ++++++--- .github/workflows/test.yaml | 22 ++++++++++++-- README.md | 9 ++++++ action.yml | 19 ++++++++++++ entrypoint.sh | 4 +-- handlers/fmt_handler.sh | 12 ++++---- handlers/init_handler.sh | 10 +++--- handlers/plan_handler.sh | 16 +++++----- handlers/tflint_handler.sh | 10 +++--- handlers/validate_handler.sh | 10 +++--- utilities/comment_utility.sh | 47 +++++++++++++++++------------ utilities/parse_args.sh | 17 ++++++----- 13 files changed, 130 insertions(+), 67 deletions(-) diff --git a/.github/workflows/release-beta.yaml b/.github/workflows/release-beta.yaml index a743ff2..a8dc3d3 100644 --- a/.github/workflows/release-beta.yaml +++ b/.github/workflows/release-beta.yaml @@ -3,15 +3,18 @@ name: Release Beta on: pull_request: +permissions: + contents: write + jobs: release-beta: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set Beta Git tag uses: weareyipyip/walking-tag-action@v2 with: tag-name: v3-beta tag-message: The current beta is based on this commit env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 365fae1..66b2f7b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -2,20 +2,24 @@ name: Release on: push: - branches: [ master ] + branches: [master] + +permissions: + contents: write + packages: write jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Bump version and push tag id: tag_version - uses: mathieudutour/github-tag-action@v6.0 + uses: mathieudutour/github-tag-action@v6.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} release_branches: "master" - - uses: sersoft-gmbh/running-release-tags-action@v1 + - uses: sersoft-gmbh/running-release-tags-action@v2 with: tag: ${{ steps.tag_version.outputs.new_tag }} - github-token: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file + github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 22f948d..7929f1b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,6 +3,10 @@ name: Test on: pull_request: +permissions: + checks: write + pull-requests: write + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TERRAFORM_VERSION: "1.4.6" @@ -11,8 +15,8 @@ env: GH_ACCEPT_HEADER: "Accept: application/vnd.github+json" GH_AUTH_HEADER: "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" GH_API_VERSION: "X-GitHub-Api-Version: 2022-11-28" - GH_COMMENT_URL: https://api.github.com/repos/GetTerminus/terraform-pr-commenter/issues/${{ github.event.number }}/comments - TESTING: "false" #set to false when finished testing + GH_COMMENT_URL: https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.number }}/comments + TESTING: "true" #set to false when finished testing jobs: set-outputs: @@ -70,6 +74,17 @@ jobs: - uses: actions/checkout@v3 if: ${{ env.TESTING == 'true' }} - name: Test + if: ${{ env.TESTING == 'true' }} + id: test + uses: GetTerminus/terraform-pr-commenter@v3-beta #set to your branch for testing and switch back to v3-beta when done. + with: + commenter_type: plan + # Should only be setting commenter_input or commenter_plan_path (commenter_plan_path only for plan commenter type) + commenter_input: ${{ needs.set-outputs.outputs.tf_plan_success_with_outputs }} + #commenter_plan_path: ./testing/text-files/tf_plan_success_with_outputs.txt + commenter_exitcode: 2 + use_beta_version: "true" + - name: Test-project-2 if: ${{ env.TESTING == 'true' }} uses: GetTerminus/terraform-pr-commenter@v3-beta #set to your branch for testing and switch back to v3-beta when done. with: @@ -79,3 +94,6 @@ jobs: #commenter_plan_path: ./testing/text-files/tf_plan_success_with_outputs.txt commenter_exitcode: 2 use_beta_version: "true" + project: dev-team-2 + - name: test output + run: echo "${{ steps.test.outputs.comment_id }}" diff --git a/README.md b/README.md index dce799d..04500ee 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ jobs: | `commenter_exitcode` | ___required___ | The exit code from a previous step output. | | `terraform_version` | ___optional___ | The version of terraform from the workflow. Defaults to `1.4.6`. | | `use_beta_version` | ___optional___ | Whether or not to use the beta version of the commenter. | +| `project` | ___optional___ | Project name to use in comments header. usefull for monorepos | ### Environment Variables @@ -122,6 +123,14 @@ jobs: | `HIGHLIGHT_CHANGES` | ___optional___ | Default: `true`. This switches `~` to `!` in `plan` diffs to highlight Terraform changes in orange. Set to `false` to disable. | | `COMMENTER_DEBUG` | ___optional___ | Default: `false`. This switches the commenter into debug mode. | +### Outputs + +| Name | Description | +|---|---| +| `comment_id` | ID of comment created by step. Can be used for further manipulations with comment | +| `comment_url` | URL pointing to comment created by step. Can be used to create a link from summary or to send a notification | + + ## Notes * The commenter requires a pull request to run so the github event must contain a `.pull_request.number`. diff --git a/action.yml b/action.yml index a4decbc..1053143 100644 --- a/action.yml +++ b/action.yml @@ -25,6 +25,17 @@ inputs: description: 'Whether to use the beta version of the commenter' required: false default: 'false' + project: + description: project name to use in the comment + required: false +outputs: + comment_id: + description: ID of created comment + value: ${{ steps.commenter-plan.outputs.comment_id || steps.commenter.outputs.comment_id }} + comment_url: + description: URL to created comment + value: ${{ steps.commenter-plan.outputs.comment_url || steps.commenter.outputs.comment_url }} + runs: using: "composite" steps: @@ -49,14 +60,17 @@ runs: shell: bash - name: Run commenter image (plan) if: ${{ inputs.commenter_type == 'plan' }} + id: commenter-plan env: COMMENTER_INPUT: ${{ inputs.commenter_input }} COMMENTER_PLAN_FILE: ${{ inputs.commenter_plan_path }} GITHUB_EVENT: ${{ toJSON(github.event) }} + TF_PROJECT: ${{ inputs.project }} run: | docker run \ -e GITHUB_TOKEN \ -e TF_WORKSPACE \ + -e TF_PROJECT \ -e EXPAND_SUMMARY_DETAILS \ -e HIGHLIGHT_CHANGES \ -e GITHUB_EVENT \ @@ -66,22 +80,27 @@ runs: -e COMMENTER_PLAN_FILE \ -e COMMENTER_POST_PLAN_OUTPUTS \ -v "$(pwd)"/:/workspace \ + -v "$GITHUB_OUTPUT":/github-ouput \ commenter ${{ inputs.commenter_type }} ${{ inputs.commenter_exitcode }} shell: bash - name: Run commenter image (non-plan) if: ${{ inputs.commenter_type != 'plan' }} + id: commenter env: COMMENTER_INPUT: ${{ inputs.commenter_input }} GITHUB_EVENT: ${{ toJSON(github.event) }} + TF_PROJECT: ${{ inputs.project }} run: | docker run \ -e GITHUB_TOKEN \ -e TF_WORKSPACE \ + -e TF_PROJECT \ -e EXPAND_SUMMARY_DETAILS \ -e HIGHLIGHT_CHANGES \ -e GITHUB_EVENT \ -e COMMENTER_INPUT \ -e COMMENTER_DEBUG \ -e COMMENTER_ECHO \ + -v "$GITHUB_OUTPUT":/github-ouput \ commenter ${{ inputs.commenter_type }} ${{ inputs.commenter_exitcode }} shell: bash diff --git a/entrypoint.sh b/entrypoint.sh index 5f9f530..e8c85b9 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # shellcheck source=handler/ -for HF in handlers/* ; do source "$HF" ; done +for HF in handlers/*; do source "$HF"; done # shellcheck source=utilities/ -for UF in utilities/* ; do source "$UF" ; done +for UF in utilities/*; do source "$UF"; done if [ -n "${COMMENTER_ECHO+x}" ]; then set -x diff --git a/handlers/fmt_handler.sh b/handlers/fmt_handler.sh index 03401f4..bfda3ed 100644 --- a/handlers/fmt_handler.sh +++ b/handlers/fmt_handler.sh @@ -1,4 +1,4 @@ -execute_fmt () { +execute_fmt() { delete_existing_comments 'fmt' '### Terraform `fmt` Failed' # Exit Code: 0 @@ -16,27 +16,27 @@ execute_fmt () { fi } -fmt_success () { +fmt_success() { info "Terraform fmt completed with no errors. Continuing." } -fmt_fail () { +fmt_fail() { local pr_comment # Exit Code: 1, 2 # Meaning: 1 = Malformed Terraform CLI command. 2 = Terraform parse error. # Actions: Build PR comment. if [[ $EXIT_CODE -eq 1 || $EXIT_CODE -eq 2 ]]; then - pr_comment=$(make_details_with_header "Terraform \`fmt\` Failed" "$INPUT") + pr_comment=$(make_details_with_header "Terraform \`fmt\` Failed ❌" "$INPUT") fi # Exit Code: 3 # Meaning: One or more files are incorrectly formatted. # Actions: Iterate over all files and build diff-based PR comment. if [[ $EXIT_CODE -eq 3 ]]; then - pr_comment=$(make_details_with_header "Terraform \`fmt\` Failed" "$INPUT" "diff") + pr_comment=$(make_details_with_header "Terraform \`fmt\` Failed ❌" "$INPUT" "diff") fi # Add fmt failure comment to PR. make_and_post_payload "fmt failure" "$pr_comment" -} \ No newline at end of file +} diff --git a/handlers/init_handler.sh b/handlers/init_handler.sh index 037b518..39e13ba 100644 --- a/handlers/init_handler.sh +++ b/handlers/init_handler.sh @@ -1,4 +1,4 @@ -execute_init () { +execute_init() { delete_existing_comments "init" '### Terraform `init` Failed' # Exit Code: 0 @@ -16,12 +16,12 @@ execute_init () { fi } -init_success () { +init_success() { info "Terraform init completed with no errors. Continuing." } -init_fail () { - local pr_comment=$(make_details_with_header "Terraform \`init\` Failed" "$INPUT") +init_fail() { + local pr_comment=$(make_details_with_header "Terraform \`init\` Failed ❌" "$INPUT") make_and_post_payload "init failure" "$pr_comment" -} \ No newline at end of file +} diff --git a/handlers/plan_handler.sh b/handlers/plan_handler.sh index 0789724..7aa61fd 100644 --- a/handlers/plan_handler.sh +++ b/handlers/plan_handler.sh @@ -1,4 +1,4 @@ -execute_plan () { +execute_plan() { # shellcheck disable=SC2016 delete_existing_comments 'plan' '### Terraform `plan` .* for Workspace: `'"$WORKSPACE"'`.*' delete_existing_comments 'outputs' '### Changes to outputs for Workspace: `'"$WORKSPACE"'`.*' @@ -18,14 +18,14 @@ execute_plan () { fi } -plan_success () { +plan_success() { post_plan_comments if [[ $POST_PLAN_OUTPUTS == 'true' ]]; then post_outputs_comments fi } -plan_fail () { +plan_fail() { local clean_input local delimiter_start_cmd local delimiter_start_strings=() @@ -39,10 +39,10 @@ plan_fail () { clean_input=$(echo "$INPUT" | perl -pe "${delimiter_start_cmd}") - post_diff_comments "plan" "Terraform \`plan\` Failed for Workspace: \`$WORKSPACE\`" "$clean_input" + post_diff_comments "plan" "Terraform \`plan\` Failed for Workspace: \`$WORKSPACE\` ❌" "$clean_input" } -post_plan_comments () { +post_plan_comments() { local clean_input local delimiter_start_strings=() local delimiter_start_cmd @@ -62,7 +62,7 @@ post_plan_comments () { clean_input=$(echo "$INPUT" | perl -pe "${delimiter_start_cmd}") clean_input=$(echo "$clean_input" | sed -r "${delimiter_end_cmd}") - post_diff_comments "plan" "Terraform \`plan\` Succeeded for Workspace: \`$WORKSPACE\`" "$clean_input" + post_diff_comments "plan" "Terraform \`plan\` Succeeded for Workspace: \`$WORKSPACE\` ✅" "$clean_input" } post_outputs_comments() { @@ -82,5 +82,5 @@ post_outputs_comments() { clean_input=$(echo "$INPUT" | perl -pe "${delimiter_start_cmd}") clean_input=$(echo "$clean_input" | sed -r "${delimiter_end_cmd}") - post_diff_comments "outputs" "Changes to outputs for Workspace: \`$WORKSPACE\`" "$clean_input" -} \ No newline at end of file + post_diff_comments "outputs" "Changes to outputs for Workspace: \`$WORKSPACE\` ⚠️" "$clean_input" +} diff --git a/handlers/tflint_handler.sh b/handlers/tflint_handler.sh index fba8516..2ed411c 100644 --- a/handlers/tflint_handler.sh +++ b/handlers/tflint_handler.sh @@ -1,4 +1,4 @@ -execute_tflint () { +execute_tflint() { # shellcheck disable=SC2016 delete_existing_comments 'tflint' '### Linter `TFLint` .* for Workspace: `'"$WORKSPACE"'`.*' @@ -17,14 +17,14 @@ execute_tflint () { fi } -tflint_success () { - info "TFLint completed with no errors. Continuing." +tflint_success() { + info "TFLint completed with no errors. Continuing." } -tflint_fail () { +tflint_fail() { local pr_comment - pr_comment=$(make_details_with_header "Linter \`TFLint\` Failed for Workspace: \`$WORKSPACE\`" "$INPUT") + pr_comment=$(make_details_with_header "Linter \`TFLint\` Failed for Workspace: \`$WORKSPACE\` ❌" "$INPUT") make_and_post_payload "tflint failure" "$pr_comment" } diff --git a/handlers/validate_handler.sh b/handlers/validate_handler.sh index 4e14f9e..b9e4f3a 100644 --- a/handlers/validate_handler.sh +++ b/handlers/validate_handler.sh @@ -1,4 +1,4 @@ -execute_validate () { +execute_validate() { delete_existing_comments "validate" '### Terraform `validate` Failed' # Exit Code: 0 @@ -16,14 +16,14 @@ execute_validate () { fi } -validate_success () { +validate_success() { info "Terraform validate completed with no errors. Continuing." } -validate_fail () { +validate_fail() { local pr_comment - pr_comment=$(make_details_with_header "Terraform \`validate\` Failed" "$INPUT" "diff") + pr_comment=$(make_details_with_header "Terraform \`validate\` Failed ❌" "$INPUT" "diff") make_and_post_payload "validate failure" "$pr_comment" -} \ No newline at end of file +} diff --git a/utilities/comment_utility.sh b/utilities/comment_utility.sh index c86dbdb..b688aba 100644 --- a/utilities/comment_utility.sh +++ b/utilities/comment_utility.sh @@ -1,5 +1,5 @@ ### PAYLOAD UTILITIES ### -make_and_post_payload () { +make_and_post_payload() { # Add plan comment to PR. local kind=$1 local pr_payload=$(echo '{}' | jq --arg body "$2" '.body = $body') @@ -9,12 +9,16 @@ make_and_post_payload () { if [[ $COMMENTER_DEBUG == true ]]; then post_comment else - post_comment > /dev/null + post_comment >/dev/null fi } make_details_with_header() { local header="### $1" + if [[ ! -z $PROJECT ]]; then + header="## Project: $PROJECT +$header" + fi local body=$2 local format=$3 local pr_comment="$header @@ -35,13 +39,15 @@ $body echo "$details" } -post_comment () { - curl -sS -L -X POST -H "$ACCEPT_HEADER" -H "$AUTH_HEADER" -H "$CONTENT_HEADER" "$PR_COMMENTS_URL" -d "$pr_payload" +post_comment() { + local comment=$(curl -sS -L -X POST -H "$ACCEPT_HEADER" -H "$AUTH_HEADER" -H "$CONTENT_HEADER" "$PR_COMMENTS_URL" -d "$pr_payload") + echo "comment_id=$(echo $comment | jq -r '.id')" >>/github-ouput + echo "comment_url=$(echo $comment | jq -r '.html_url')" >>/github-ouput } ### DIFF AND STRING SUBSTITUTION UTILITIES ### -post_diff_comments () { +post_diff_comments() { local type=$1 local comment_prefix=$2 local comment_string=$3 @@ -58,7 +64,7 @@ post_diff_comments () { local colorized_comment=$(substitute_and_colorize "$current") local comment_count_text="" if [ "$comment_count" -ne 1 ]; then - comment_count_text=" ($((i+1))/$comment_count)" + comment_count_text=" ($((i + 1))/$comment_count)" fi local comment=$(make_details_with_header "$comment_prefix$comment_count_text" "$colorized_comment" "diff") @@ -66,16 +72,16 @@ post_diff_comments () { done } -substitute_and_colorize () { +substitute_and_colorize() { local current_plan=$1 - current_plan=$(echo "$current_plan" | sed -r 's/^([[:blank:]]*)([😅+~])/\2\1/g' | sed -r 's/^😅/-/') + current_plan=$(echo "$current_plan" | sed -r 's/^([[:blank:]]*)([😅+~])/\2\1/g' | sed -r 's/^😅/-/') if [[ $COLOURISE == 'true' ]]; then current_plan=$(echo "$current_plan" | sed -r 's/^~/!/g') # Replace ~ with ! to colourise the diff in GitHub comments fi echo "$current_plan" } -delimiter_start_cmd_builder () { +delimiter_start_cmd_builder() { local delimiter_string delimiter_string=$(print_start_delimiter_string "$@") @@ -84,24 +90,23 @@ delimiter_start_cmd_builder () { printf "\$_=\"\" unless /(%s)/ .. 1" "$delimiter_string" } -delimiter_end_cmd_builder () { +delimiter_end_cmd_builder() { local delimiter_string printf "/%s/q" "$1" } -print_start_delimiter_string () -{ +print_start_delimiter_string() { # run through array and print each entry: local array array=("$@") - for i in "${array[@]}" ; do + for i in "${array[@]}"; do printf '%s|' "$i" done } ### DELETE UTILITIES ### -delete_existing_comments () { +delete_existing_comments() { # Look for an existing PR comment and delete # debug "Existing comments: $(curl -sS -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -L $PR_COMMENTS_URL)" @@ -109,6 +114,10 @@ delete_existing_comments () { local regex=$2 local last_page + if [[ ! -z $PROJECT ]]; then + regex="## Project: $PROJECT\\n$regex" + fi + debug "Type: $type" debug "Regex: $regex" @@ -123,22 +132,20 @@ delete_existing_comments () { info "Looking for an existing $type PR comment." local comment_ids=() - for page in $(seq $last_page) - do + for page in $(seq $last_page); do # first, we read *all* of the comment IDs across all pages. saves us from the problem where we read a page, then # delete some, then read the next page, *after* our page boundary has moved due to the delete. - # CAUTION. this line assumes the PR_COMMENTS_URL already has at least one query parameter. (note the '&') + # CAUTION. this line assumes the PR_COMMENTS_URL already has at least one query parameter. (note the '&') readarray -t -O "${#comment_ids[@]}" comment_ids < <(curl -sS -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -L "$PR_COMMENTS_URL&page=$page" | jq "$jq") done - for PR_COMMENT_ID in "${comment_ids[@]}" - do + for PR_COMMENT_ID in "${comment_ids[@]}"; do FOUND=true info "Found existing $type PR comment: $PR_COMMENT_ID. Deleting." PR_COMMENT_URL="$PR_COMMENT_URI/$PR_COMMENT_ID" STATUS=$(curl -sS -X DELETE -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -o /dev/null -w "%{http_code}" -L "$PR_COMMENT_URL") debug "Status: $STATUS" - if [ "$STATUS" != "204" ]; then + if [ "$STATUS" != "204" ]; then info "Failed to delete: status $STATUS (most likely rate limited)" fi done diff --git a/utilities/parse_args.sh b/utilities/parse_args.sh index 5a27e8c..c287e41 100644 --- a/utilities/parse_args.sh +++ b/utilities/parse_args.sh @@ -1,25 +1,25 @@ ################## # Shared Variables ################## -parse_args () { +parse_args() { # Arg 1 is command COMMAND=$1 debug "COMMAND: $COMMAND" - # Arg 3 is the Terraform CLI exit code + # Arg 2 is the Terraform CLI exit code EXIT_CODE=$2 debug "EXIT_CODE: $EXIT_CODE" - # Arg 2 is input file. We strip ANSI colours. + # COMMENTER_INPUT is input file. We strip ANSI colours. RAW_INPUT="$COMMENTER_INPUT" debug "COMMENTER_INPUT: $COMMENTER_INPUT" if [[ $COMMAND == 'plan' ]]; then if test -f "workspace/${COMMENTER_PLAN_FILE}"; then info "Found commenter plan file." - pushd workspace > /dev/null || (error "Failed to push workspace dir" && exit 1) - RAW_INPUT="$( cat "${COMMENTER_PLAN_FILE}" 2>&1 )" - popd > /dev/null || (error "Failed to pop workspace dir" && exit 1) + pushd workspace >/dev/null || (error "Failed to push workspace dir" && exit 1) + RAW_INPUT="$(cat "${COMMENTER_PLAN_FILE}" 2>&1)" + popd >/dev/null || (error "Failed to pop workspace dir" && exit 1) else info "Found no tfplan file. Using input argument." fi @@ -45,6 +45,9 @@ parse_args () { # Read TF_WORKSPACE environment variable or use "default" # shellcheck disable=SC2034 WORKSPACE=${TF_WORKSPACE:-default} + # Read TF_WORKSPACE environment variable or use "" + # shellcheck disable=SC2034 + PROJECT=${TF_PROJECT:-''} # Read EXPAND_SUMMARY_DETAILS environment variable or use "true" if [[ ${EXPAND_SUMMARY_DETAILS:-false} == "true" ]]; then @@ -74,4 +77,4 @@ parse_args () { # shellcheck disable=SC2034 PR_COMMENT_URI=$(echo "$GITHUB_EVENT" | jq -r ".repository.issue_comment_url" | sed "s|{/number}||g") -} \ No newline at end of file +}