Feedback Forms: Moving to triggered GH Actions

Published: Mar 3, 2023 by Isaac Johnson

Earlier this week, we covered updating Azure DevOps webhook triggered pipelines to update Azure Work Items and create a Github Issue. The goal was to update our flows to start to use Github Issues over AzDO. Today, we’ll continue this journey as we focus on moving into Github Actions. We’ll use OIDC to auth to AKV for some secrets and learn how to parse out the GH JSON results to share the new Issue ID.

Lastly, we’ll tackle feedback forms including adding Authentication headers and wrap with creating and using a new Slack App to update a Slack channel.

The repos used below, if you wish to follow along:

Github Action creation

First, I’ll create a new GH Repo for this work

/content/images/2023/03/ghwfupdates-01.png

Next, I’ll clone and create a new workflow file

/content/images/2023/03/ghwfupdates-02.png

on:
  repository_dispatch:
    types: [on-demand-test]

jobs:
  run_if_payload:
    if: ${{ github.event }}
    runs-on: ubuntu-latest
    steps:
      - run: |
          set -x
          export
      - env:
          MESSAGE: ${{ github.event.message }}
        run: echo $MESSAGE

  run_if_failure:
    if: ${{ !github.event }}
    runs-on: ubuntu-latest
    steps:
      - run: |
          set -x
          export
      - env:
          MESSAGE: ${{ github.event.client_payload.message }}
        run: echo $MESSAGE

Unlike AzDO, we cannot invoke a Github webhook anonymously. It requires a Github PAT. That said, we can use the newer Narrowly Scoped PATs to reduce the abilities to the bare minimum.

There was no documentation on what it actually took to invoke a Webhook. I did many many trial-and-errors to get to the final set of permissions. Most came back with a with a response of:

$ curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer github_pat_asdfsadfasdfasdfasdsadf" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/idjohnson/workflowTriggerTest/dispatches -d '{"event_type":"on-demand-payload","client_payload":{"summary":"this is the summary","description":"this is the descriptionn"}}'
{
  "message": "Resource not accessible by personal access token",
  "documentation_url": "https://docs.github.com/rest/reference/repos#create-a-repository-dispatch-event"
}

to get there.

However, I narrowed it down to the following:

A fine-grained token limited to just the one repository

/content/images/2023/03/ghwfupdates-03.png

And then only two permissions are required:

  • Contents: Read and write
  • Metadata: Read-only

/content/images/2023/03/ghwfupdates-04.png

Now when I invoke the Webhook

$ curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer 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" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/idjohnson/workflowTriggerTest/dispatches -d '{"event_type":"on-demand-payload","client_payload":{"summary":"this is the summary","description":"this is the descriptionn"}}'

I can see the messages passed forward

/content/images/2023/03/ghwfupdates-05.png

Webform

Our next step is to move this out of Curl and use a webform

I needed to create a feedback form, I named src/feedback2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="css/styles.css">
    <title>Form to API</title>
</head>
<body>
    <div class="container card card-color">
        <form action="" id="sampleForm">
            <h2>Create a feedback task</h2>
            <div class="form-row">
                <label for="userId">Email Address</label>
                <input type="email" class="input-text input-text-block w-100" id="userId" name="userId" value="anonymous@dontemailme.com">
            </div>
            <div class="form-row">
                <label for="summary">Summary</label>
                <input type="text" class="input-text input-text-block w-100" id="summary" name="summary">
            </div>
            <div class="form-row">
                <label for="description">Description or Details</label>
                <textarea class="input-text input-text-block ta-100" id="description" name="description"></textarea>
            </div>
            <div class="form-row mx-auto">
                <button type="submit" class="btn-submit" id="btnSubmit">
                Submit
                </button>
            </div>
        </form>
      </div>
      <script src="js/app.js"></script>
</body>
</html>

My src/js/app.js had a slew of changes.

async function submitForm(e, form) {
    // 1. Prevent reloading page
    e.preventDefault();
    // 2. Submit the form
    // 2.1 User Interaction
    const btnSubmit = document.getElementById('btnSubmit');
    btnSubmit.disabled = true;
    setTimeout(() => btnSubmit.disabled = false, 2000);
    // 2.2 Build JSON body
    const jsonFormData = buildJsonFormData(form);
    // 2.3 Build Headers
    const headers = buildHeaders();
    // 2.4 Request & Response
    const response = await fetchService.performPostHttpRequest(`https://api.github.com/repos/idjohnson/workflowTriggerTest/dispatches`, headers, jsonFormData); // Uses JSON Placeholder
    console.log(response);
    // 2.5 Inform user of result
    if(response)
       window.location = `/success.html`;
    else
       alert(`An error occured.`);
}

function buildHeaders() {
    const headers = {
        "Content-Type": "application/vnd.github+json",
        "Authorization": "Bearer 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"
    };
    return headers;
}

function buildJsonFormData(form) {
    const jsonRetData = { "event_type": "on-demand-payload" };
    const jsonFormData = { };
    for(const pair of new FormData(form)) {
        jsonFormData[pair[0]] = pair[1];
    }
    jsonRetData.client_payload = jsonFormData;
    console.log(jsonRetData);
    return jsonRetData;
}
/*--/Functions--*/

/*--Event Listeners--*/
const sampleForm = document.querySelector("#sampleForm");
if(sampleForm) {
    sampleForm.addEventListener("submit", function(e) {
        submitForm(e, this);
    });
}

The key areas I needed to change:

  • the URL in fetchService.performPostHttpRequest.
  • I removed the fields in success.html
  • buildHeaders function removed auth set to null, added the bearer token (with low privs) and set the Content type.
  • buildJsonFormData actually took me a bit to figure out - but I wanted to dynamically set the nested “client_payload” sub array.

Then src/js/service/fetchService.js stubbed out the json parsing on the response in performPostHttpRequest

    async performPostHttpRequest(fetchLink, headers, body) {
        if(!fetchLink || !headers || !body) {
            throw new Error("One or more POST request parameters was not passed.");
        }
        try {
            const rawResponse = await fetch(fetchLink, {
                method: "POST",
                headers: headers,
                body: JSON.stringify(body)
            });
            console.log(rawResponse);
            // Webhooks are not JSON
            // const content = await rawResponse.json();
            //return content;
            return rawResponse;
        }
        catch(err) {
            console.error(`Error at fetch POST: ${err}`);
            throw err;
        }
    }

My last step was to just make sure scripts/dev in package.json served up the new HTML (feedback2.html)

  "scripts": {
    "dev": "npm run clean && parcel src/index.html --out-dir dev feedback2.html",

I finally got a successful result by running in dev

builder@DESKTOP-QADGF36:~/Workspaces/submitformtojsonapi$ npm run dev

> submittingformstoapis@1.0.0 dev /home/builder/Workspaces/submitformtojsonapi
> npm run clean && parcel src/index.html --out-dir dev feedback2.html


> submittingformstoapis@1.0.0 clean /home/builder/Workspaces/submitformtojsonapi
> rimraf ./dev && rimraf -rf ./.cache

Server running at http://localhost:1234 
⠹ Building app.js...Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
⠸ Building app.js...Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
⠴ Building runtime.js...Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
✨  Built in 1.13s.
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
✨  Built in 57ms.

And testing the form:

/content/images/2023/03/ghwfupdates-06.png

This fired a build

/content/images/2023/03/ghwfupdates-07.png

to which I could see results

/content/images/2023/03/ghwfupdates-08.png

At this point, the pipeline is fairly “hello world”. All it really does is print back the elements as passed

on:
  repository_dispatch:
    types: [on-demand-payload]

jobs:
  run_if_payload:
    if: $
    runs-on: ubuntu-latest
    steps:
      - run: |
          set -x
          export
      - env:
          MESSAGE: $
        run: echo $MESSAGE
      - env:
          MESSAGE: $
        run: echo $MESSAGE

Clearly to have some fun adding more Issues, triggering Slack and Discord, and sending emails, we’re going to need to save aside some secrets

Sticking with an ephemeral pipeline, this is a great excuse to try out what we went over last week on OIDC.

I’ll head to the Azure Portal and go to App Registrations:

/content/images/2023/03/ghwfupdates-09.png

and I’ll add a credential

/content/images/2023/03/ghwfupdates-10.png

Then select Github Actions creating Azure Resources

/content/images/2023/03/ghwfupdates-11.png

Then we’ll add our WorkflowTest repo and branch

/content/images/2023/03/ghwfupdates-12.png

We’ll now test

      - name: 'Az CLI login'
        uses: azure/login@v1
        with:
          client-id: '$'
          tenant-id: '$'
          subscription-id: '$'
  
      - name: 'Run az commands'
        run: |
          az account show
          az group list

I’ll set those variables as new Github Actions secrets

/content/images/2023/03/ghwfupdates-13.png

The first time testing, I got an error

Using OIDC authentication...
Error:  Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable. Please make sure to give write permissions to id-token in the workflow.

I needed to add at the top

permissions:
   id-token: write
   contents: read

which unblocked and let the action run without issue.

/content/images/2023/03/ghwfupdates-14.png

In our last writeup, we added an access policy to an AKV for that App Registration

      - name: 'Check AKV'
        run: |
          az keyvault secret list --vault wldemokv -o table

We can add a secret for a GH Token (or put into AKV) then build out a new GH Action that replicates what we did last time in an Azure Pipeline

on:
  repository_dispatch:
    types: [on-demand-payload]

permissions:
   id-token: write
   contents: read

jobs:
  run_if_payload:
    if: $
    runs-on: ubuntu-latest
    steps:
      - run: |
          set -x
          export
      - env:
          MESSAGE: $
        run: echo $MESSAGE
      - env:
          MESSAGE: $
        run: echo $MESSAGE
      - name: 'Az CLI login'
        uses: azure/login@v1
        with:
          client-id: '$'
          tenant-id: '$'
          subscription-id: '$'
      - name: 'Run az commands'
        run: |
          az account show
          az group list
      - name: 'Check AKV'
        run: |
          az keyvault secret show --vault-name wldemokv --name sessmtppass -o json | jq -r .value > sessmtppass
          az keyvault secret show --vault-name wldemokv --name sessmtpuser -o json | jq -r .value > sessmtpuser
          
          sudo apt-get update
          sudo apt-get install -y s-nail || true

          cat >rawDescription <<EOOOOL
          $
          EOOOOL

          cat >rawSummary <<EOOOOT
          $
          EOOOOT
          
          set -x
          cat rawSummary |sed ':a;N;$!ba;s/\n/ /g' | sed 's/"/\\"/g' > emailSummary
          
          data="$( jq -nc --arg title "$" --arg body "$ :: Requested by $" '$ARGS.named')"
          cat >emailJson2.json <<EOTT
          $data
          EOTT

          curl -X POST -H "Accept: application/vnd.github+json" \
              -H "Authorization: Bearer $" \
              -H "X-GitHub-Api-Version: 2022-11-28" \
              https://api.github.com/repos/idjohnson/jekyll-blog/issues -d @emailJson2.json > gh.o.json

          export USERTLD=`echo "$" | sed 's/^.*@//'`

          export GHURL=`cat gh.o.json | jq -r .html_url`

          # Now Send Email
          if [[ "$USERTLD" == "dontemailme.com" ]]; then
            # do not CC user
            echo "<h1>New Feature Requested</h1><p>user $ has requested "`cat emailSummary`"</p><p>$GHURL</p><br/><br/>Kind Regards,<br/>Isaac Johnson" | s-nail -s "Blog: Feature $WIID Requested" -M "text/html" -S smtp=email-smtp.us-east-1.amazonaws.com:587 -S smtp-use-starttls -S ssl-verify=ignore -S smtp-auth=login -S smtp-auth-user=`cat sessmtpuser` -S smtp-auth-password=`cat sessmtppass` -c isaac.johnson@gmail.com -r isaac@freshbrewed.science isaac.johnson@gmail.com
          else
            # may not work
            echo "<h1>New Feature Requested</h1><p>user $ has requested "`cat emailSummary`"</p><p>$GHURL</p><br/><br/>Kind Regards,<br/>Isaac Johnson" | s-nail -s "Blog: Feature $WIID Requested" -M "text/html" -S smtp=email-smtp.us-east-1.amazonaws.com:587 -S smtp-use-starttls -S ssl-verify=ignore -S smtp-auth=login -S smtp-auth-user=`cat sessmtpuser` -S smtp-auth-password=`cat sessmtppass` -c $ -r isaac@freshbrewed.science isaac.johnson@gmail.com
          fi

  run_if_failure:
    runs-on: ubuntu-latest
    needs: run_if_payload
    if: always() && (needs.run_if_payload.result == 'failure')
    steps:
      - run: |
          az keyvault secret show --vault-name wldemokv --name sessmtppass -o json | jq -r .value > sessmtppass
          az keyvault secret show --vault-name wldemokv --name sessmtpuser -o json | jq -r .value > sessmtpuser
          
          sudo apt-get update
          sudo apt-get install -y s-nail || true
          
          export USERTLD=`echo "$" | sed 's/^.*@//'`
          echo "<h1>New Feature Requested</h1><p>user $ but the formatting must have failed.  Please check the pipeline!" | s-nail -s "Blog: Feature Requested (FAIL processing)" -M "text/html" -S smtp=email-smtp.us-east-1.amazonaws.com:587 -S smtp-use-starttls -S ssl-verify=ignore -S smtp-auth=login -S smtp-auth-user=`cat sessmtpuser` -S smtp-auth-password=`cat sessmtppass` -c isaac.johnson@gmail.com -r isaac@freshbrewed.science isaac.johnson@gmail.com
      - env:
          MESSAGE: $
        run: echo $MESSAGE

/content/images/2023/03/ghwfupdates-15.png

Let’s now give that a test. First, locally

$ curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer 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" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/idjohnson/workflowTriggerTest/dispatches -d '{"event_type":"on-demand-payload","client_payload":{"summary":"Github Actions","description":"Let'\''s Do more Github Actions","userid":"isa
ac.johnson@gmail.com"}}'

which triggers the pipeline

/content/images/2023/03/ghwfupdates-16.png

creates the GH Issue

/content/images/2023/03/ghwfupdates-17.png

and sends the email

/content/images/2023/03/ghwfupdates-18.png

Slack app

First, we need to Login to Slack in a browser and go to the API endpoint to create a new API app

/content/images/2023/03/ghwfupdates-19.png

I’ll give it a name and select a workspace

/content/images/2023/03/ghwfupdates-20.png

Next, I need to install to a workspace

/content/images/2023/03/ghwfupdates-23.png

I’ll choose “#Builds#

/content/images/2023/03/ghwfupdates-24.png

That gets us a Webhook URL

/content/images/2023/03/ghwfupdates-25.png

Which I can test with the URL

$ curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' https://hooks.slack.com/services/asdfasdfsadf/asdfasdfasdf1/asdfasdfasdfsadf

which I can now see

/content/images/2023/03/ghwfupdates-26.png

I’ll add to AKV

/content/images/2023/03/ghwfupdates-27.png

Finishing the feedback form.

Since I don’t really want to loose my work so far (as I did before). I’ll fork the original repo to https://github.com/idjohnson/submitformtojsonapi. I’ve left it public so you can follow along.

I propagated the new files

builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   package.json
        modified:   src/css/styles.css
        modified:   src/index.html
        modified:   src/js/app.js
        modified:   src/js/service/FetchService.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        src/feedback2.html

no changes added to commit (use "git add" and/or "git commit -a")

Then I’ll run install and test dev

builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi$ npm install

> deasync@0.1.20 install /home/builder/Workspaces/ijohnson-submitformtojsonapi/node_modules/deasync
> node ./build.js

`linux-x64-node-10` exists; testing
Binary is fine; exiting

> core-js@2.6.11 postinstall /home/builder/Workspaces/ijohnson-submitformtojsonapi/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon:
> https://opencollective.com/core-js
> https://www.patreon.com/zloirock

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)


> parcel-bundler@1.12.4 postinstall /home/builder/Workspaces/ijohnson-submitformtojsonapi/node_modules/parcel-bundler
> node -e "console.log('\u001b[35m\u001b[1mLove Parcel? You can now donate to our open collective:\u001b[22m\u001b[39m\n > \u001b[34mhttps://opencollective.com/parcel/donate\u001b[0m')"

Love Parcel? You can now donate to our open collective:
 > https://opencollective.com/parcel/donate
npm WARN submittingformstoapis@1.0.0 No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

added 768 packages from 463 contributors and audited 770 packages in 10.187s

23 packages are looking for funding
  run `npm fund` for details

found 133 vulnerabilities (13 low, 61 moderate, 49 high, 10 critical)
  run `npm audit fix` to fix them, or `npm audit` for details
builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi$ npm run dev

> submittingformstoapis@1.0.0 dev /home/builder/Workspaces/ijohnson-submitformtojsonapi
> npm run clean && parcel src/index.html --out-dir dev feedback2.html


> submittingformstoapis@1.0.0 clean /home/builder/Workspaces/ijohnson-submitformtojsonapi
> rimraf ./dev && rimraf -rf ./.cache

Server running at http://localhost:35663 - configured port 1234 could not be used.
⠙ Building app.js...Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
⠼ Building runtime.js...Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
✨  Built in 1.12s.
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating

And I can see that running

/content/images/2023/03/ghwfupdates-28.png

The background colour and fonts aren’t quite right.

/content/images/2023/03/ghwfupdates-29.png

I pulled the existing CSS with view-source:https://freshbrewed.science/styles.c9ed72d6.css then used pretty print to format so I could compare and fix.

That now looks right

/content/images/2023/03/ghwfupdates-30.png

I’ll now test before bringing into this repo

builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi/dev$ aws s3 cp --recursive --acl public-read-write ./ s3://freshbrewed-test/
upload: ./styles.b61e60ae.css to s3://freshbrewed-test/styles.b61e60ae.css
upload: ./feedback.html to s3://freshbrewed-test/feedback.html
upload: ./styles.b61e60ae.css.map to s3://freshbrewed-test/styles.b61e60ae.css.map
upload: ./styles.b61e60ae.js to s3://freshbrewed-test/styles.b61e60ae.js
upload: ./styles.b61e60ae.js.map to s3://freshbrewed-test/styles.b61e60ae.js.map
upload: ./app.c3f9f951.js.map to s3://freshbrewed-test/app.c3f9f951.js.map
upload: ./app.c3f9f951.js to s3://freshbrewed-test/app.c3f9f951.js

And test

/content/images/2023/03/ghwfupdates-31.png

Which worked

/content/images/2023/03/ghwfupdates-32.png

And email

/content/images/2023/03/ghwfupdates-33.png

I see two last minor issues - I need to fix the GH URL and add the Slack work we did.

The diff looks as

$ git diff
diff --git a/.github/workflows/triggerTest.yml b/.github/workflows/triggerTest.yml
index 9e4c482..1cb065a 100644
--- a/.github/workflows/triggerTest.yml
+++ b/.github/workflows/triggerTest.yml
@@ -61,7 +61,15 @@ jobs:

           export USERTLD=`echo "$" | sed 's/^.*@//'`

-          export GHURL=`cat gh.o.json | jq -r .url`
+          export GHURL=`cat gh.o.json | jq -r .html_url`
+
+          # Slack Notification
+          slackdata="$( jq -nc --arg text "Feature Request $GHURL: $ Requested by $" '$ARGS.named')"
+          cat >slackjson.json <<EOTT
+          $slackdata
+          EOTT
+
+          curl -X POST -H 'Content-type: application/json' `az keyvault secret show --vault-name wldemokv --name slackbuildswh -o json | jq -r .value | tr -d '\n'` -d @slackjson.json

           # Now Send Email
           if [[ "$USERTLD" == "dontemailme.com" ]]; then
@@ -71,10 +79,6 @@ jobs:
             # may not work
             echo "<h1>New Feature Requested</h1><p>user $ has requested "`cat emailSummary`"</p><p>$GHURL</p><br/><br/>Kind Regards,<br/>Isaac Johnson" | s-nail -s "Blog: Feature $WIID Requested" -M "text/html" -S smtp=email-smtp.us-east-1.amazonaws.com:587 -S smtp-use-starttls -S ssl-verify=ignore -S smtp-auth=login -S smtp-auth-user=`cat sessmtpuser` -S smtp-auth-password=`cat sessmtppass` -c $ -r isaac@freshbrewed.science isaac.johnson@gmail.com
           fi
-
-          az keyvault secret show --vault-name wldemokv --name slackbuildswh -o json | jq -r .value > slackbuildswh
-
-

   run_if_failure:
     runs-on: ubuntu-latest
@@ -89,7 +93,15 @@ jobs:
           sudo apt-get install -y s-nail || true

           export USERTLD=`echo "$" | sed 's/^.*@//'`
-          echo "<h1>New Feature Requested</h1><p>user $ but the formatting must have failed.  Please check the pipeline!" | s-nail -s "Blog: Feature Requested (FAIL processing)" -M "text/html" -S smtp=email-smtp.us-east-1.amazonaws.com:587 -S smtp-use-starttls -S ssl-verify=ignore -S smtp-auth=login -S smtp-auth-user=`cat sessmtpuser` -S smtp-auth-password=`cat sessmtppass` -c isaac.johnson@gmail.com -r isaac@freshbrewed.science isaac.johnson@gmail.com
+          echo "<h1>New Feature Requested</h1><p>user $ but the formatting must have failed.  Please check the pipeline: Run ID $GITHUB_RUN_ID!" | s-nail -s "Blog: Feature Requested (FAIL processing)" -M "text/html" -S smtp=email-smtp.us-east-1.amazonaws.com:587 -S smtp-use-starttls -S ssl-verify=ignore -S smtp-auth=login -S smtp-auth-user=`cat sessmtpuser` -S smtp-auth-password=`cat sessmtppass` -c isaac.johnson@gmail.com -r isaac@freshbrewed.science isaac.johnson@gmail.com
+
+          # Slack Notification
+          slackdata="$( jq -nc --arg text "Feature Request Issue Create Failure: see $GITHUB_JOB on Run $GITHUB_RUN_ID :: requested by $" '$ARGS.named')"
+          cat >slackjson.json <<EOTT
+          $slackdata
+          EOTT
+
+          curl -X POST -H 'Content-type: application/json' `az keyvault secret show --vault-name wldemokv --name slackbuildswh -o json | jq -r .value | tr -d '\n'` -d @slackjson.json
       - env:
           MESSAGE: $
         run: echo $MESSAGE

The key things was fixing the URL to ‘.html_url’ from the GH payload and adding Slack notifications. I also noted the failed run ID should it fail.

I’ll use a more complicated test this time

/content/images/2023/03/ghwfupdates-34.png

I can now see that worked in Slack

/content/images/2023/03/ghwfupdates-35.png

In email

/content/images/2023/03/ghwfupdates-36.png

and as a Github Issue

/content/images/2023/03/ghwfupdates-37.png

Feedback Form and Tokens

Tokens have expirations. As it stands, if I do nothing my feedback form will stop working in less than 90 days.

/content/images/2023/03/ghwfupdates-38.png

And any updates necessarily create new tokens (which mean new builds of the static form)

/content/images/2023/03/ghwfupdates-39.png

At best I can generate one a Year out. I did that but then noted the Expiry in AKV

/content/images/2023/03/ghwfupdates-40.png

I did an update and then rebuilt and pushed, including to Production (after I tested)

builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi/dev$ mv index.html feedback.html
builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi/dev$ aws s3 cp --recursive --acl public-read-write ./ s3://freshbrewed-test/
upload: ./styles.b61e60ae.css.map to s3://freshbrewed-test/styles.b61e60ae.css.map
upload: ./feedback.html to s3://freshbrewed-test/feedback.html
upload: ./styles.b61e60ae.css to s3://freshbrewed-test/styles.b61e60ae.css
upload: ./styles.b61e60ae.js.map to s3://freshbrewed-test/styles.b61e60ae.js.map
upload: ./app.c3f9f951.js.map to s3://freshbrewed-test/app.c3f9f951.js.map
upload: ./styles.b61e60ae.js to s3://freshbrewed-test/styles.b61e60ae.js
upload: ./app.c3f9f951.js to s3://freshbrewed-test/app.c3f9f951.js
builder@DESKTOP-QADGF36:~/Workspaces/ijohnson-submitformtojsonapi/dev$ aws s3 cp --recursive --acl public-read-write ./ s3://freshbrewed.science/
upload: ./styles.b61e60ae.css.map to s3://freshbrewed.science/styles.b61e60ae.css.map
upload: ./styles.b61e60ae.js to s3://freshbrewed.science/styles.b61e60ae.js
upload: ./styles.b61e60ae.css to s3://freshbrewed.science/styles.b61e60ae.css
upload: ./feedback.html to s3://freshbrewed.science/feedback.html
upload: ./styles.b61e60ae.js.map to s3://freshbrewed.science/styles.b61e60ae.js.map
upload: ./app.c3f9f951.js to s3://freshbrewed.science/app.c3f9f951.js
upload: ./app.c3f9f951.js.map to s3://freshbrewed.science/app.c3f9f951.js.map

Lastly, I’ll delete the old token so any exposed GHP token here has been expired

/content/images/2023/03/ghwfupdates-41.png

Summary

In this entry we tackled a lot - we covered moving from the Azure DevOps workflow into a new Github Action in a new GH Repo. We covered secrets on the repo as well as federated access to Azure Key Vault shared secrets via OIDC using a new repo+branch endpoint.

Next, we tackled setting up Slack using the App workflow and creating the HTML static form to Post to our payload. This included handling additional authentication headers (which the anonymous endpoints of Azure DevOps let us avoid prior).

I had to put a pin in the next steps; I want to add back Teams integration and add Hosted JIRA as well since a lot of folks out there use the Atlassian suites.

One limitation here is that I necessarily had to expose my Github Token on a specific repo. Ideally, I would abstract that somehow - by either triggering a lambda that would re-wrap the contents and pass them forward or coming up with another trigger. However, IF I had to move to a new serverless “runner”, then I likely wouldn’t two-step - I would just write the logic up as an Azure Function, AWS Lambda or a GCP Cloud Function. That is, there would be no need to keep a Github flow if I already have a place to do work.

Feedback Github Issues Actions

Have something to add? Feedback? Try our new forums

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes