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