Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions .github/DISCUSSION_TEMPLATE/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,62 @@ body:
- type: markdown
attributes:
value: |
### Important News for Actions users 📣 👀
#### ⚠️ [Upcoming deprecation of v3 of the artifact actions](https://github.com/orgs/community/discussions/142581) - upgrade to v4 as soon as possible.
🚀 **Welcome to the Actions Category!** 🚀

Automate, customize, and execute your software development workflows right in your repository with [GitHub Actions](https://docs.github.com/en/actions)!

**Why are you here?**
- **Have a Question?** Maybe you're stuck on workflow syntax, curious about best practices, or want to know how to use a specific GitHub Actions feature. Select "Question" to get help from the community!
- **Product Feedback?** Do you have ideas to improve GitHub Actions, or want to share what works (or doesn't) for you? Select "Product Feedback" to help shape the future of GitHub Actions.
- **Found a Bug?** If something isn't working as expected, let us know by selecting "Bug" so we can investigate and improve the platform.

After choosing your reason, pick the topic or product area that best matches your discussion.

**Quick links:** [Actions Documentation](https://docs.github.com/en/actions), [Workflow Syntax Guide](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions), [Example Workflows](https://docs.github.com/en/actions/use-cases-and-examples), and [Security Best Practices](https://docs.github.com/en/actions/security-for-github-actions)

**Recent News:**
- [Latest Breaking Changes](https://github.blog/changelog/label/actions)

- type: dropdown
id: reason
attributes:
label: Select Topic Area
description: What would you like to discuss?
label: Why are you starting this discussion?
description: Tell us your goal! Are you asking a question, giving feedback, reporting a bug, or sharing something cool?
options:
- "Question"
- "Product Feedback"
- "Bug"
- "Show & Tell"
- "General"
default: 0
validations:
required: true
- type: dropdown
id: topic
attributes:
label: What GitHub Actions topic or product is this about?
description: |
Choose the single topic or product area most relevant to your discussion. Only one can be selected here.

If your post covers additional themes or topics, don't worry! Our team and automation will do our best to add extra labels based on the content of your discussion details.

This helps us get you the best answers and feedback!
options:
- "ARC (Actions Runner Controller)"
- "Actions Runner Image"
- "Actions Runner"
- "Actions Checkout"
- "Actions Cache"
- "Workflow Deployment"
- "Workflow Configuration"
- "Schedule & Cron Jobs"
- "Metrics & Insights"
- "Misc"
default: 9
validations:
required: false
- type: textarea
attributes:
label: Body
description: Start your discussion!
label: Discussion Details
description: >-
Share your question, feedback, or story! Include links, code, or screenshots for more context. Tip: Reference the docs https://docs.github.com/en/actions or relevant changelogs for details.
validations:
required: true
required: true
303 changes: 303 additions & 0 deletions .github/workflows/actions_labeller.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
name: Auto-Label Discussions for Actions Category

on:
discussion:
types: [created]

jobs:
label-actions-discussion:
if: ${{ contains(github.event.discussion.category.name, 'Actions') }}
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Global authentication for gh CLI
steps:
- name: Get discussion body html
id: get_discussion_body_html
env:
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
run: |
gh api graphql -F owner=$OWNER -F name=$REPO -F number=$DISCUSSION_NUMBER -f query='
query($owner: String!, $name: String!, $number: Int!) {
repository(owner: $owner, name: $name){
discussion(number: $number) {
bodyHTML
id
}
}
}' > discussion_data.json

echo 'DISCUSSION_BODY_HTML='$(jq -r '.data.repository.discussion.bodyHTML' discussion_data.json) >> $GITHUB_ENV
echo 'DISCUSSION_ID='$(jq -r '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV

- run: npm install jsdom


- name: Extract Title and Body Text
id: extract_text
uses: actions/github-script@v6
env:
DISCUSSION_BODY_HTML: ${{ env.DISCUSSION_BODY_HTML }}
DISCUSSION_TITLE: ${{ github.event.discussion.title }}
with:
result-encoding: string
script: |
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { DISCUSSION_BODY_HTML } = process.env;
const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML);
let body = '';
// Find all <h3> and <p> pairs
const h3s = Array.from(fragment.querySelectorAll('h3'));
h3s.forEach(h3 => {
const heading = h3.textContent.trim();
let p = h3.nextElementSibling;
while (p && p.tagName !== 'P') p = p.nextElementSibling;
if (!p) return;
if (heading === 'Discussion Details') {
body = p.textContent.trim();
}
});
// Remove leading/trailing quotes from body
body = body.replace(/^['\"]+|['\"]+$/g, '');
const title = process.env.DISCUSSION_TITLE || '';
core.info(`Extracted title: ${title}`);
core.info(`Extracted body: ${body}`);
return JSON.stringify({ title, body });

- name: Extract Primary and Secondary Topic Areas
id: extract_topics
uses: actions/github-script@v6
with:
result-encoding: string
script: |
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { DISCUSSION_BODY_HTML } = process.env;
const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML);
let primary = '';
let secondary = '';
// Find all <h3> and <p> pairs (form headings as h3, answers as p)
const h3s = Array.from(fragment.querySelectorAll('h3'));
h3s.forEach(h3 => {
const heading = h3.textContent.trim();
// Look for the next <p> sibling after each heading
let p = h3.nextElementSibling;
while (p && p.tagName !== 'P') p = p.nextElementSibling;
if (!p) return;
if (heading === 'Why are you starting this discussion?') {
primary = p.textContent.trim();
}
if (heading === 'What GitHub Actions topic or product is this about?') {
secondary = p.textContent.trim();
}
});
core.info(`Extracted primary topic: ${primary}`);
core.info(`Extracted secondary topic: ${secondary}`);
return JSON.stringify({ primary, secondary });

- name: Auto-label by keyword search
id: auto_label_keywords
uses: actions/github-script@v6
with:
result-encoding: string
script: |
// Keyphrase to label mapping
const labelMap = [
{
label: 'Workflow Deployment',
keywords: [
"deployment error",
"publish artifact",
"release failure",
"deployment target",
"github pages",
"deployment issue",
"release workflow",
"target environment"
]
},
{
label: 'Workflow Configuration',
keywords: [
"yaml syntax",
"job dependency",
"setup error",
"workflow file",
"configuration issue",
"matrix strategy",
"define env",
"secret management",
"environment setup",
"config job"
]
},
{
label: 'Schedule & Cron Jobs',
keywords: [
"cron job",
"scheduled workflow",
"timing issue",
"delay trigger",
"timezone error",
"periodic run",
"recurring schedule",
"interval workflow",
"scheduled trigger",
"cron expression"
]
},
{
label: 'Metrics & Insights',
keywords: [
"usage metrics",
"performance trend",
"analytics graph",
"stats dashboard",
"timeseries graph",
"insight report",
"metric tracking",
"workflow analytics",
"performance metric",
"statistics report"
]
}
];
const miscLabel = 'Misc';
let title = '';
let body = '';
try {
const parsed = JSON.parse(`${{ steps.extract_text.outputs.result }}`);
title = parsed.title || '';
body = parsed.body || '';
} catch (e) {}
const text = (title + ' ' + body).toLowerCase();
let foundLabel = miscLabel;
core.info(`Auto-label debug: text to match: '${text}'`);
for (const map of labelMap) {
core.info(`Auto-label debug: checking label '${map.label}' with keywords: ${map.keywords.join(', ')}`);
for (const k of map.keywords) {
if (text.includes(k)) {
core.info(`Auto-label debug: matched keyword '${k}' for label '${map.label}'`);
foundLabel = map.label;
break;
}
}
if (foundLabel !== miscLabel) break;
}
core.info(`Auto-label debug: selected label: '${foundLabel}'`);
return foundLabel;
- name: Fetch label ID for primary topic
id: fetch_primary_label_id
env:
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
TOPIC: ${{ fromJson(steps.extract_topics.outputs.result).primary }}
run: |
echo "DEBUG: Fetching label for primary topic: $TOPIC"
gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query='
query($owner: String!, $name: String!, $topic: String) {
repository(owner: $owner, name: $name) {
labels(first: 1, query: $topic) {
edges {
node {
id
name
}
}
}
}
}
' > primary_label_data.json

PRIMARY_LABEL_ID=$(jq -r '.data.repository.labels.edges[0]?.node?.id // empty' primary_label_data.json)
echo "PRIMARY_LABEL_ID=$PRIMARY_LABEL_ID" >> $GITHUB_ENV

- name: Fetch label ID for secondary topic
id: fetch_secondary_label_id
env:
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
TOPIC: ${{ fromJson(steps.extract_topics.outputs.result).secondary }}
run: |
echo "DEBUG: Fetching label for secondary topic: $TOPIC"
gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query='
query($owner: String!, $name: String!, $topic: String) {
repository(owner: $owner, name: $name) {
labels(first: 1, query: $topic) {
edges {
node {
id
name
}
}
}
}
}
' > secondary_label_data.json

SECONDARY_LABEL_ID=$(jq -r '.data.repository.labels.edges[0]?.node?.id // empty' secondary_label_data.json)
echo "SECONDARY_LABEL_ID=$SECONDARY_LABEL_ID" >> $GITHUB_ENV
- name: Fetch label ID for auto-label
id: fetch_auto_label_id
env:
OWNER: ${{ github.repository_owner }}
REPO: ${{ github.event.repository.name }}
TOPIC: ${{ steps.auto_label_keywords.outputs.result }}
run: |
gh api graphql -F owner=$OWNER -F name=$REPO -F topic="$TOPIC" -f query='
query($owner: String!, $name: String!, $topic: String) {
repository(owner: $owner, name: $name) {
labels(first: 1, query: $topic) {
edges {
node {
id
name
}
}
}
}
}' > auto_label_data.json

AUTO_LABEL_ID=$(jq -r '.data.repository.labels.edges[0]?.node?.id // empty' auto_label_data.json)
echo "AUTO_LABEL_ID=$AUTO_LABEL_ID" >> $GITHUB_ENV

- name: Apply labels to discussion
if: ${{ env.PRIMARY_LABEL_ID != '' || env.SECONDARY_LABEL_ID != '' || env.AUTO_LABEL_ID != '' }}
run: |
echo "DEBUG: PRIMARY_LABEL_ID=$PRIMARY_LABEL_ID"
echo "DEBUG: SECONDARY_LABEL_ID=$SECONDARY_LABEL_ID"
echo "DEBUG: AUTO_LABEL_ID=$AUTO_LABEL_ID"
LABEL_IDS=()
if [ -n "$PRIMARY_LABEL_ID" ]; then
LABEL_IDS+=("$PRIMARY_LABEL_ID")
fi
if [ -n "$SECONDARY_LABEL_ID" ]; then
LABEL_IDS+=("$SECONDARY_LABEL_ID")
fi
if [ -n "$AUTO_LABEL_ID" ]; then
LABEL_IDS+=("$AUTO_LABEL_ID")
fi

# Deduplicate LABEL_IDS
LABEL_IDS=($(printf "%s\n" "${LABEL_IDS[@]}" | awk '!seen[$0]++'))
echo "DEBUG: LABEL_IDS to apply: ${LABEL_IDS[@]}"

# Apply labels
gh api graphql -f query='
mutation($labelableId: ID!, $labelIds: [ID!]!) {
addLabelsToLabelable(input: {labelableId: $labelableId, labelIds: $labelIds}) {
labelable {
labels(first: 10) {
edges {
node {
id
name
}
}
}
}
}
}
' -f labelableId=$DISCUSSION_ID $(printf -- "-f labelIds[]=%s " "${LABEL_IDS[@]}")
Loading