Skip to content
Open
151 changes: 151 additions & 0 deletions .github/workflows/auto-answer-issues.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: Auto-Answer Issues

on:
issues:
types: [opened, labeled]

permissions:
issues: write
contents: read
Comment on lines +7 to +9
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job requests contents: read, but this workflow doesn’t appear to need repository contents (it only installs npm packages and calls GitHub/Azure OpenAI). For least-privilege, consider removing contents: read unless a later step truly requires it.

Copilot uses AI. Check for mistakes.

jobs:
answer-issue:
runs-on: ubuntu-latest
# Only run for issues created by org members or owners (i.e., Microsoft Open Source enterprise members).
# github.event.issue.author_association is set by GitHub based on the issue author's relationship
# to this repository. MEMBER = org member, OWNER = repo/org owner. This prevents untrusted
# external contributors from triggering the Azure OpenAI-backed responder and consuming secrets/tokens.
Comment on lines +14 to +17
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment claims this only runs for org members/owners and equates that to “Microsoft Open Source enterprise members”, but github.event.issue.author_association reflects repo/org relationship (not GitHub Enterprise membership). Please adjust this comment to accurately describe what’s enforced so readers don’t assume a stronger guarantee than exists.

Suggested change
# Only run for issues created by org members or owners (i.e., Microsoft Open Source enterprise members).
# github.event.issue.author_association is set by GitHub based on the issue author's relationship
# to this repository. MEMBER = org member, OWNER = repo/org owner. This prevents untrusted
# external contributors from triggering the Azure OpenAI-backed responder and consuming secrets/tokens.
# Only run for issues created by users who have a trusted relationship to this repo/org in GitHub
# (based on github.event.issue.author_association), such as MEMBER, OWNER, or COLLABORATOR.
# author_association reflects the issue author's relationship to this repository/organization only;
# it does NOT indicate GitHub Enterprise or "Microsoft Open Source enterprise" membership.

Copilot uses AI. Check for mistakes.
if: |
github.event.issue.author_association == 'MEMBER' ||
github.event.issue.author_association == 'OWNER' ||
github.event.issue.author_association == 'COLLABORATOR'
steps:
Comment on lines +18 to +22
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The explanatory comment says “Only run for issues created by org members or owners”, but the if: condition also allows COLLABORATOR. Either update the comment to include collaborators, or tighten the condition to match the stated restriction.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +22
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says the automation is restricted to “members, owners, collaborators, and contributors”, but the workflow guard currently does not include CONTRIBUTOR. Either update the PR description to match the implementation, or add CONTRIBUTOR back if it’s intended to be allowed.

Copilot uses AI. Check for mistakes.
- name: Checkout repository
uses: actions/checkout@v4

Comment on lines +23 to +25
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actions/checkout step is not used by subsequent steps (the script is inline and dependencies are installed from the registry). Removing checkout reduces runtime and avoids granting/using repository content access unnecessarily.

Suggested change
- name: Checkout repository
uses: actions/checkout@v4

Copilot uses AI. Check for mistakes.
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm install @octokit/rest openai

- name: Generate and post response
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
AZURE_OPENAI_ENDPOINT: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
AZURE_OPENAI_DEPLOYMENT: ${{ secrets.AZURE_OPENAI_DEPLOYMENT }}
AZURE_OPENAI_API_VERSION: "2024-12-01-preview"
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
run: |
node - << 'EOF'
async function main() {
// Use dynamic import() for ESM-only packages (openai v4+ and @octokit/rest v19+
// are ESM-only; dynamic import() works from CommonJS in Node 20).
const { Octokit } = await import("@octokit/rest");
const { AzureOpenAI } = await import("openai");
const {
GITHUB_TOKEN,
AZURE_OPENAI_API_KEY,
AZURE_OPENAI_ENDPOINT,
AZURE_OPENAI_DEPLOYMENT,
AZURE_OPENAI_API_VERSION,
ISSUE_NUMBER,
ISSUE_TITLE,
ISSUE_BODY,
REPO_OWNER,
REPO_NAME
} = process.env;

if (!GITHUB_TOKEN) {
throw new Error("GITHUB_TOKEN is not set.");
}
if (!AZURE_OPENAI_API_KEY) {
throw new Error("AZURE_OPENAI_API_KEY is not set.");
}
if (!AZURE_OPENAI_ENDPOINT) {
throw new Error("AZURE_OPENAI_ENDPOINT is not set.");
}
if (!AZURE_OPENAI_DEPLOYMENT) {
throw new Error("AZURE_OPENAI_DEPLOYMENT is not set.");
}
if (!ISSUE_NUMBER || !REPO_OWNER || !REPO_NAME) {
throw new Error("Required issue/repository environment variables are missing.");
}

const issueNumber = parseInt(ISSUE_NUMBER, 10);
const title = ISSUE_TITLE || "";
const body = ISSUE_BODY || "";

const octokit = new Octokit({ auth: GITHUB_TOKEN });

// Check if the bot has already commented on this issue to avoid duplicate responses.
const comments = await octokit.paginate(octokit.issues.listComments, {
owner: REPO_OWNER,
repo: REPO_NAME,
issue_number: issueNumber,
per_page: 100
});
const botAlreadyCommented = comments.some(
(comment) => comment.user?.login === "github-actions[bot]"
Comment on lines +94 to +96
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prompt hard-codes “An issue has been opened…”, but the workflow also runs on the labeled event. Consider adjusting the prompt text based on github.event.action so the model has accurate context (e.g., “opened” vs “labeled for auto-answer”).

Copilot uses AI. Check for mistakes.
);
if (botAlreadyCommented) {
console.log("Bot has already commented on this issue. Skipping.");
return;
}
Comment on lines +88 to +101
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicate-response check treats any existing comment by github-actions[bot] as “already answered”. That’s overly broad (other workflows/actions can comment as github-actions[bot]) and can prevent this workflow from ever posting. Consider adding a unique marker to this workflow’s comment (e.g., an HTML comment tag) and checking for that marker instead.

Copilot uses AI. Check for mistakes.

const openai = new AzureOpenAI({
apiKey: AZURE_OPENAI_API_KEY,
endpoint: AZURE_OPENAI_ENDPOINT,
deployment: AZURE_OPENAI_DEPLOYMENT,
apiVersion: AZURE_OPENAI_API_VERSION
});

const prompt = `
You are a helpful GitHub assistant. An issue has been opened in the repository \`${REPO_OWNER}/${REPO_NAME}\`.

Title:
${title}

Body:
${body}

Please write a concise, friendly reply that:
- Acknowledges the question or issue.
- Asks for any missing information if needed.
- Suggests next steps or possible causes based only on the information provided.
- Uses markdown formatting suitable for a GitHub issue comment.
`;

const completion = await openai.chat.completions.create({
model: AZURE_OPENAI_DEPLOYMENT,
messages: [
Comment on lines +123 to +128
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow always posts a new comment for every eligible opened/labeled event. There’s currently no guard for a specific label name and no check for whether the bot has already commented, so repeated labeling (or reopening) can spam issues and burn Azure OpenAI tokens. Consider (a) gating on a dedicated label (e.g. only when action == 'labeled' && label.name == 'auto-answer'), and/or (b) listing existing issue comments via Octokit and exiting early if a prior auto-answer marker/comment from the bot is already present.

Copilot uses AI. Check for mistakes.
{ role: "system", content: "You are a helpful assistant for triaging GitHub issues." },
{ role: "user", content: prompt }
]
});

const reply = (completion.choices[0]?.message?.content || "").trim();
if (!reply) {
throw new Error("Azure OpenAI returned an empty response.");
}

await octokit.issues.createComment({
owner: REPO_OWNER,
repo: REPO_NAME,
issue_number: issueNumber,
body: reply
});
}

main().catch((error) => {
console.error("Failed to generate or post response:", error);
process.exit(1);
});
EOF
Loading