Automating with GitHub issue forms
David Rodríguez Donoso
Posted on June 12, 2023
If you are familiar with issue templates, it is a good way to customise templates whenever anyone wants to create a new issue.
Issue templates need a markdown file, so when a user opens a new issue, it has a template in markdown, so the user needs to modify whatever he wants to have it in the same format.
- What is the bad point about that?
+ A user can modify the template provided, change the headings, or completely remove everything and create a new issue.
A user that creates a new issue with this kind of template can manipulate the issue to change anything that is provided by the template.
New issue forms
So, what are issue forms for?
Issue forms are a new type of issue template. With issue forms, you can create customizable web form fields. So you have structured information on the issues you need.
Issue forms are currently in beta.
You should have it in the same folder as the issue templates, but issue forms have a YAML extension. So, whenever a user needs to open a new issue, the user cannot change headings or completely remove everything, he won't be so evil.
Here we have a form that the user can fill out and select multiple values from, so the form will always be the same.
And this is totally amazing! Why? From here, it is so easy to automate anything you need.
So, let's say you need to run an script based on the inputs the user opening the form is providing. We can run a workflow from the issue created event, and the event body has the same format for every issue created; we can pick all the values provided and work with that.
Ok, but how? I created a repository called IssueChatbotTemplate so you can copy the workflow.
Let's analyze it here:
---
name: issue
run-name:
on:
issues:
types: [opened]
jobs:
issue:
name: Get inputs from issue form
runs-on: ubuntu-latest
if: ${{ contains(github.event.issue.title , '[Test Issue]')}}
timeout-minutes: 30
steps:
- name: Add Comment
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Workflow running on: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}',
});
- name: Get inputs
id: inputs
shell: bash
run: |
add_output(){
local k=$1
local v=$2
echo "$k=$v" >> $GITHUB_OUTPUT
echo "✅ Output generated with key: $k, value: $v"
}
process_key(){
local process_key=$1
# Trim leading and trailing whitespace
process_key="$(echo -e "${process_key}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
# Substitute spaces with underscores
process_key="${process_key// /_}"
# Convert to lowercase
process_key="${process_key,,}"
echo "$process_key";
}
process_checkbox_line(){
local checkbox_line=$1
option=$(echo "$checkbox_line" | sed 's/- \[[^]]*\] //')
key="$(process_key "$option")"
value=$(echo "$checkbox_line" | grep -qE -- '- \[X\]' && echo "true" || echo "false")
add_output "$key" "$value"
escaped_lookfor=$(echo "$checkbox_line" | sed 's/[][()\.^$?*+{}|]/\\&/g')
nextcheckbox=$(echo "${{ github.event.issue.body }}" | awk "/$escaped_lookfor$/{getline; print}")
if [[ "$nextcheckbox" == "- ["* && -n "$nextcheckbox" && "$nextcheckbox" != "$checkbox_line" ]]; then
process_checkbox_line "$nextcheckbox"
fi
}
echo "${{ github.event.issue.body }}" | awk '/^###/ { print }' | while read line; do
echo "🔍 Processing line: $line"
value=$(echo "${{ github.event.issue.body }}" | awk "/^$line$/{getline;getline; print}")
if [[ "$value" == "- ["* ]]; then
# Processing a checkbox
echo "☑️ Detected a checkbox!"
process_checkbox_line "$value"
else
# Remove "###" from the beginning of the string
key=${line##"###"}
key="$(process_key "$key")"
add_output "$key" "$value"
fi
done
- uses: actions/github-script@v6
name: Add label
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ["environment/${{steps.inputs.outputs.dropdown_label}}"]
});
outputs:
input: ${{ steps.inputs.outputs.input_label }}
dropdown: ${{ steps.inputs.outputs.dropdown_label }}
dropdown_multiple: ${{ steps.inputs.outputs.dropdown_multiple_label }}
textarea: ${{ steps.inputs.outputs.textarea_label }}
checkbox1: ${{ steps.inputs.outputs.checkbox_label_1 }}
checkbox2: ${{ steps.inputs.outputs.checkbox_label_2 }}
process:
name: Process ${{needs.issue.outputs.dropdown}}
runs-on: ubuntu-latest
needs: issue
steps:
- name: Add Comment
uses: actions/github-script@v6
with:
script: |
const body = `### Processed outputs:
**input_label**: ${{ needs.issue.outputs.input }}
**dropdown_label**: ${{ needs.issue.outputs.dropdown }}
**dropdown_multiple_label**: ${{ needs.issue.outputs.dropdown_multiple }}
**textarea_label**: ${{ needs.issue.outputs.textarea }}
**checkbox_label_1**: ${{ needs.issue.outputs.checkbox1 }}
**checkbox_label_2**: ${{ needs.issue.outputs.checkbox2 }}
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
Here I'm running the workflow whenever a new issue is created; if the issue has the title [Test Issue] I run the first job (Yes, the user can remove the [Test Issue] title, you can manage it with labels, for example. And yes, the user can remove the label also.).
Then, in the first step, I'm adding a comment to the same issue, so the user knows the workflow is running.
The next step is where magic happens: grabbing all inputs and adding them as GitHub outputs with the label formatted and the value.
Also, I'm adding some labels to the issue based on the input provided, so for example, you can know which environment is faster. Adding outputs for this operation is the final step in the job. That's where the work should end.
And finally, in a new job, adding a comment with the id and the value known in the same issue (this is something you probably won't need, just for debugging purposes).
So, as a result, we will have something like that:
And this is just a start point, from here you can grow it by calling another job to deploy the application to some environment, running an automation to grant some permissions for some user (For security changes, you will need to check the user creating the issue, of course), or any other idea you want to automate.
Also, you can improve it. So let's say you have like 3-4 issue forms in the new template, so you have 3-4 workflows for these issue forms.
Whenever you open a new issue, all workflows will be executed, but not the jobs. So you can improve it by changing the event of these workflows to workflow_call and having 1 main workflow that adds the first comment in the issue, and then with a strategy calling the workflow you want to execute based on title, labels, etc...
Thanks for your time reading this! I highly appreciate any feedback on any of my social media accounts or here.
Posted on June 12, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.