Skip to content

GitHub Action, Bash Script, and PR Comment – Oh My!

The Use Case

Recently I was tasked with parsing some XML, returning non-conforming URL rewrite rules, and alerting on them within a GitHub Action. You see, in a project I was working on we had a URL rule that was a partial match for another route, and it caused some data issues in an API call. This was an edge case – but we wanted to strengthen our URL rewrite rules .. so we added ^ and $ to the existing rules, and the follow up task was to create something automated to make sure if we write anything loose .. that it gets reported on. Having never written my own GitHub Action, I dove down the rabbit hole of examples, documentation, and seeing what path I could take.

After digging through JavaScript, Docker, and composite runs .. I went with the simplest/easiest possible method: A bash script that returns results, formats them, and then outputs them to a PR comment. Why not fail the PR? Well – in some cases, you may have a legitimate reason for bypassing the warning.

The Bash Script

First step, creating the bash script, script.sh. This will go into .github/workflows in your GitHub repository.

#!/bin/bash       

# Detect issues in URL Rewrite Rules        

set -euo pipefail       

output=""

for file in $(find /home/runner/work/UrlRewriteRuleCheckingGHAction/UrlRewriteRuleCheckingGHAction -name 'Web.config')
do       
        lineNumber=1       

        for line in $(xmlstarlet sel -T -t -m /configuration/system.webServer/rewrite/rules/rule/match/@url -v . -n "$file")       
        do       
                if [[ $line != \^* ]] || [[ $line != *\$ ]]                       
                then       
                        output+="Line Item: <br /><br /> $line <br /><br /> in $file on line $lineNumber;"       
                fi       

                ((lineNumber+=1))
        done       
done       
echo "::set-output name=ERROR_LINES::${output::-1}"

So what’s going on here?

for file in $(find /home/runner/work/project/project -name 'Web.config')

Loop through ANY Web.config in our repository. This can be customized to target a specific file. We have multiple configs that could have rules to check.

for line in $(xmlstarlet sel -T -t -m /configuration/system.webServer/rewrite/rules/rule/match/@url -v . -n \"$file\")

xmlstarlet allows us to target specific XML nodes and get the value.

if [[ $line != \\^* ]] || [[ $line != *\\$ ]]
then                        
output+=\"Line Item: <br /><br /> $line <br /><br /> in $file on line $lineNumber;\"
fi

We’re checking to make sure every single line starts with ^ and ends with $. If either of these are false, we add the line and line number to the output.

And finally …

echo \"::set-output name=ERROR_LINES::${output::-1}\"

Set a variable, ERROR_LINES, for consumption outside of the subshell.

The GitHub Action File

The next file is validate_rewrite_rules.yml, which we will put in the same directory as the script.sh file.

name: URLRewriteValidator               
on:
pull_request:            
    branches:         
      - main      
jobs:        
  url_rule_checker: 
    runs-on: ubuntu-latest  
    steps:       
      - uses: actions/checkout@01aecccf739ca6ff86c0539fbc67a7a5007bbc81  
      - name: Install xmlstarlet       
        run: sudo apt-get install xmlstarlet  
      - name: Query against Web.config       
        id: url_rule_checker       
        run: |       
          # Grant perms       
          chmod +x "${GITHUB_WORKSPACE}/.github/workflows/script.sh"     
          # Run Script
          "${GITHUB_WORKSPACE}/.github/workflows/script.sh" 
      - name: Get Errors       
        run: echo "Errors in \n ${{ steps.url_rule_checker.outputs.ERROR_LINES }}" 
      - name: Format comment for PR       
        id: format_comment       
        if: steps.url_rule_checker.outputs.ERROR_LINES       
        run: |       
          COMMENT_BODY=`echo "${{ steps.url_rule_checker.outputs.ERROR_LINES }}" | sed 's/*/\\\*/g;s/;/<\/li><li>/g'`       
                
          COMMENT_BODY="There are URL rewrite URLs that dont start/end with ^ and $.  Verify that the URL rules are what you expect.<br /><ul><li>${COMMENT_BODY}</li></ul>"       
                
          echo "::set-output name=formatted_comment::$COMMENT_BODY"                   
          echo "$COMMENT_BODY" | sed 's/*/\\\*/g;s/;/<\/li><li>/g'       
      - uses: mshick/add-pr-comment@5cd99bf9c186219af43341076f1fe9c09e5a9934 # v1      
        if: steps.url_rule_checker.outputs.ERROR_LINES     
        with:       
          message: ${{ steps.format_comment.outputs.formatted_comment }}                       
          repo-token: ${{ secrets.GITHUB_TOKEN }}                       
          repo-token-user-login: 'github-actions[bot]' 
          allow-repeats: false

So let’s break it down..

name: URLRewriteValidator               
on:
pull_request:            
    branches:         
      - main  

We want to fire this action off on Pull Request.

          - uses: actions/checkout@01aecccf739ca6ff86c0539fbc67a7a5007bbc81

To quote from the Action GitHub repo:

“This action checks-out your repository under $GITHUB_WORKSPACE , so your workflow can access it. Only a single commit is fetched by default, for the ref/SHA that triggered the workflow.”

- name: Install xmlstarlet       
        run: sudo apt-get install xmlstarlet 

This installs xmlstarlet within the shell.

- name: Query against Web.config       
        id: url_rule_checker       
        run: |       
          # Grant perms       
          chmod +x "${GITHUB_WORKSPACE}/.github/workflows/script.sh"     
          # Run Script
          "${GITHUB_WORKSPACE}/.github/workflows/script.sh" 

Here we execute our script that we created.

      - name: Get Errors       
        run: echo "Errors in \n ${{ steps.url_rule_checker.outputs.ERROR_LINES }}" 

After the script runs, we now display/confirm our ERROR_LINES variable is set.

- name: Format comment for PR       
        id: format_comment       
        if: steps.url_rule_checker.outputs.ERROR_LINES       
        run: |       
          COMMENT_BODY=`echo "${{ steps.url_rule_checker.outputs.ERROR_LINES }}" | sed 's/*/\\\*/g;s/;/<\/li><li>/g'`       
                
          COMMENT_BODY="There are URL rewrite URLs that dont start/end with ^ and $.  Verify that the URL rules are what you expect.<br /><ul><li>${COMMENT_BODY}</li></ul>"       
                
          echo "::set-output name=formatted_comment::$COMMENT_BODY"                   
          echo "$COMMENT_BODY" | sed 's/*/\\\*/g;s/;/<\/li><li>/g'       

If we have ERROR_LINES, we want to parse/set the body of our comment. We are going to split on the semi-colon we insert in the script output, and insert it into List Item html tags.

- uses: mshick/add-pr-comment@5cd99bf9c186219af43341076f1fe9c09e5a9934 # v1      
        if: steps.url_rule_checker.outputs.ERROR_LINES     
        with:       
          message: ${{ steps.format_comment.outputs.formatted_comment }}                       
          repo-token: ${{ secrets.GITHUB_TOKEN }}                       
          repo-token-user-login: 'github-actions[bot]' 
          allow-repeats: false

And finally, we use a GitHub action called Add-Pr-Comment by mshick. We set the message to our formatted comment, pull the GITHUB_TOKEN from the secrets that are available via the GitHub Actions, and set the login.

Repository: https://github.com/MattTheDev/UrlRewriteRuleCheckingGHAction

Pull Request with Comments: https://github.com/MattTheDev/UrlRewriteRuleCheckingGHAction/pull/3

Published in.NET

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *