mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
fbbc2ebe86
Scopes the triage job to the integration GitHub Environment, adds the azure/login OIDC step, and exposes the same OpenAI / Azure OpenAI / Foundry / Anthropic env vars the integration test workflow uses. This lets the triage agent write repro code that constructs model clients from the environment without any secrets entering the agent prompt or generated-code literals. Azure OpenAI and Foundry continue to authenticate via AAD (DefaultAzureCredential), so there is no API key to leak for those providers.
200 lines
7.2 KiB
YAML
200 lines
7.2 KiB
YAML
name: Issue Triage
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
issue_number:
|
|
description: Issue number to triage
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
id-token: write
|
|
|
|
concurrency:
|
|
group: issue-triage-${{ github.repository }}-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
DEVFLOW_REPOSITORY: ${{ vars.DF_REPO }}
|
|
DEVFLOW_REF: main
|
|
TARGET_REPO_PATH: ${{ github.workspace }}/target-repo
|
|
DEVFLOW_PATH: ${{ github.workspace }}/devflow
|
|
|
|
jobs:
|
|
team_check:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
is_team_member: ${{ steps.check.outputs.is_team_member }}
|
|
issue_number: ${{ steps.issue.outputs.issue_number }}
|
|
repo: ${{ steps.issue.outputs.repo }}
|
|
steps:
|
|
- name: Resolve issue metadata
|
|
id: issue
|
|
shell: bash
|
|
env:
|
|
ISSUE_NUMBER_EVENT: ${{ github.event.issue.number }}
|
|
ISSUE_NUMBER_INPUT: ${{ inputs.issue_number }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then
|
|
issue_number="${ISSUE_NUMBER_EVENT}"
|
|
else
|
|
issue_number="${ISSUE_NUMBER_INPUT}"
|
|
fi
|
|
|
|
if [[ ! "$issue_number" =~ ^[1-9][0-9]*$ ]]; then
|
|
echo "Could not determine issue number; for workflow_dispatch runs, the 'issue_number' input is required." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "issue_number=${issue_number}" >> "$GITHUB_OUTPUT"
|
|
echo "repo=${GITHUB_REPOSITORY}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Checkout scripts
|
|
uses: actions/checkout@v6
|
|
with:
|
|
sparse-checkout: .github/scripts
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Check issue author team membership
|
|
id: check
|
|
uses: actions/github-script@v8
|
|
env:
|
|
TEAM_NAME: ${{ secrets.DEVELOPER_TEAM }}
|
|
ISSUE_NUMBER: ${{ steps.issue.outputs.issue_number }}
|
|
with:
|
|
github-token: ${{ secrets.GH_ACTIONS_PR_WRITE }}
|
|
script: |
|
|
const checkTeamMembership = require('./.github/scripts/check_team_membership.js');
|
|
const { author, isTeamMember } = await checkTeamMembership({
|
|
github,
|
|
context,
|
|
core,
|
|
teamSlug: process.env.TEAM_NAME,
|
|
issueNumber: process.env.ISSUE_NUMBER,
|
|
});
|
|
core.setOutput('is_team_member', isTeamMember ? 'true' : 'false');
|
|
if (isTeamMember) {
|
|
core.info(`Author ${author} is a team member; skipping auto-triage.`);
|
|
} else {
|
|
core.info(`Author ${author} is not a team member; proceeding with triage.`);
|
|
}
|
|
|
|
triage:
|
|
runs-on: ubuntu-latest
|
|
needs: team_check
|
|
if: ${{ needs.team_check.outputs.is_team_member == 'false' }}
|
|
environment: integration
|
|
timeout-minutes: 60
|
|
|
|
steps:
|
|
# Safe checkout: base repo only.
|
|
- name: Checkout target repo base
|
|
uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
path: target-repo
|
|
|
|
# Private DevFlow (maf-dashboard) checkout.
|
|
- name: Checkout DevFlow
|
|
uses: actions/checkout@v6
|
|
with:
|
|
repository: ${{ env.DEVFLOW_REPOSITORY }}
|
|
ref: ${{ env.DEVFLOW_REF }}
|
|
token: ${{ secrets.DEVFLOW_TOKEN }}
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
path: devflow
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.13"
|
|
|
|
- name: Set up uv
|
|
uses: astral-sh/setup-uv@v7
|
|
with:
|
|
version: "0.11.x"
|
|
enable-cache: true
|
|
|
|
- name: Install DevFlow dependencies
|
|
working-directory: ${{ env.DEVFLOW_PATH }}
|
|
run: uv sync --frozen
|
|
|
|
- name: Azure CLI Login
|
|
uses: azure/login@v2
|
|
with:
|
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
|
|
- name: Classify issue relevance
|
|
id: spam
|
|
working-directory: ${{ env.DEVFLOW_PATH }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
|
|
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
|
|
ISSUE_REPO: ${{ needs.team_check.outputs.repo }}
|
|
ISSUE_NUMBER: ${{ needs.team_check.outputs.issue_number }}
|
|
run: |
|
|
uv run python scripts/classify_issue_spam.py \
|
|
--repo "$ISSUE_REPO" \
|
|
--issue-number "$ISSUE_NUMBER" \
|
|
--repo-path "${TARGET_REPO_PATH}" \
|
|
--apply-labels
|
|
|
|
- name: Stop after spam gate
|
|
if: ${{ steps.spam.outputs.decision != 'allow' }}
|
|
shell: bash
|
|
env:
|
|
SPAM_DECISION: ${{ steps.spam.outputs.decision }}
|
|
run: |
|
|
echo "Stopping: spam gate decided: ${SPAM_DECISION}"
|
|
exit 1
|
|
|
|
- name: Reproduce reported issue
|
|
if: ${{ steps.spam.outputs.decision == 'allow' }}
|
|
id: repro
|
|
working-directory: ${{ env.DEVFLOW_PATH }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GH_COPILOT_TOKEN: ${{ secrets.GH_COPILOT_TOKEN }}
|
|
SK_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
|
|
AGENT_REPO_PATH: ${{ env.TARGET_REPO_PATH }}
|
|
ISSUE_REPO: ${{ needs.team_check.outputs.repo }}
|
|
ISSUE_NUMBER: ${{ needs.team_check.outputs.issue_number }}
|
|
# Model-provider settings for generated repro code. Never enter the
|
|
# agent prompt; consumed by SDK constructors via os.environ. Azure
|
|
# OpenAI and Foundry auth via AAD from the azure/login step above.
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }}
|
|
OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }}
|
|
OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
|
OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }}
|
|
OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }}
|
|
AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }}
|
|
AZURE_OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }}
|
|
AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
|
AZURE_OPENAI_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }}
|
|
AZURE_OPENAI_EMBEDDING_MODEL: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }}
|
|
FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }}
|
|
FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }}
|
|
FOUNDRY_AGENT_NAME: ${{ vars.FOUNDRY_AGENT_NAME }}
|
|
FOUNDRY_AGENT_VERSION: ${{ vars.FOUNDRY_AGENT_VERSION }}
|
|
FOUNDRY_MODELS_ENDPOINT: ${{ vars.FOUNDRY_MODELS_ENDPOINT || '' }}
|
|
FOUNDRY_MODELS_API_KEY: ${{ secrets.FOUNDRY_MODELS_API_KEY || '' }}
|
|
FOUNDRY_EMBEDDING_MODEL: ${{ vars.FOUNDRY_EMBEDDING_MODEL || '' }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
ANTHROPIC_CHAT_MODEL: ${{ vars.ANTHROPIC_CHAT_MODEL_ID }}
|
|
run: |
|
|
uv run python scripts/trigger_issue_repro.py \
|
|
--repo "$ISSUE_REPO" \
|
|
--issue-number "$ISSUE_NUMBER" \
|
|
--github-username "$GITHUB_ACTOR"
|