mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
fix: Enable batch mode for issue triage safe-outputs
- Add target: "*" to safe-outputs configuration for add-labels and add-comment - This allows the workflow to apply comments and labels to multiple issues when running in scheduled/manual trigger mode (batch processing) - Previously, safe-outputs jobs were skipped because they required github.event.issue.number which only exists in event-triggered runs - With target: "*", the workflow now reads issue numbers from the agent output and applies changes to each issue individually
This commit is contained in:
Generated
+292
-175
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Source: githubnext/agentics/workflows/issue-triage.md@0837fb7b24c3b84ee77fb7c8cfa8735c48be347a
|
||||
#
|
||||
# Effective stop-time: 2025-12-03 20:01:19
|
||||
# Effective stop-time: 2025-12-06 19:47:58
|
||||
#
|
||||
# Job Dependency Graph:
|
||||
# ```mermaid
|
||||
@@ -43,8 +43,8 @@
|
||||
# https://github.com/actions/github-script/commit/ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
# - actions/setup-node@v6 (2028fbc5c25fe9cf00d9f06a71cc4710d4507903)
|
||||
# https://github.com/actions/setup-node/commit/2028fbc5c25fe9cf00d9f06a71cc4710d4507903
|
||||
# - actions/upload-artifact@v4 (ea165f8d65b6e75b540449e92b4886f43607fa02)
|
||||
# https://github.com/actions/upload-artifact/commit/ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
# - actions/upload-artifact@v5 (330a01c490aca151604b8cf639adc76d48f6c5d4)
|
||||
# https://github.com/actions/upload-artifact/commit/330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
|
||||
name: "Agentic Triage"
|
||||
"on":
|
||||
@@ -59,12 +59,22 @@ concurrency:
|
||||
|
||||
run-name: "Agentic Triage"
|
||||
|
||||
env:
|
||||
GH_AW_ERROR_PATTERNS: |-
|
||||
[
|
||||
{"id":"gh-action-error","pattern":"^::(error)(?:\\\\s+[^:]*)?::(.+)","level_group":1,"message_group":2,"description":"GitHub Actions workflow command - error"},
|
||||
{"id":"gh-action-warning","pattern":"^::(warning)(?:\\\\s+[^:]*)?::(.+)","level_group":1,"message_group":2,"description":"GitHub Actions workflow command - warning"},
|
||||
{"id":"bracketed-level","pattern":"^\\[(ERROR|CRITICAL|WARNING|WARN)\\]\\s+(.+)","level_group":1,"message_group":2,"description":"Bracketed log level at start of line"},
|
||||
{"id":"timestamped-copilot","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z\\s+\\[(ERROR|WARN|WARNING|CRITICAL)\\]\\s+(.+)","level_group":1,"message_group":3,"description":"Timestamped Copilot CLI messages"}
|
||||
]
|
||||
|
||||
jobs:
|
||||
activation:
|
||||
needs: pre_activation
|
||||
if: needs.pre_activation.outputs.activated == 'true'
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
discussions: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
@@ -132,14 +142,22 @@ jobs:
|
||||
core.info(` Source modified: ${workflowStat.mtime.toISOString()}`);
|
||||
core.info(` Lock modified: ${lockStat.mtime.toISOString()}`);
|
||||
if (workflowMtime > lockMtime) {
|
||||
const warningMessage = `🔴🔴🔴 WARNING: Lock file '${lockFile}' is outdated! The workflow file '${workflowMdFile}' has been modified more recently. Run 'gh aw compile' to regenerate the lock file.`;
|
||||
const warningMessage = `WARNING: Lock file '${lockFile}' is outdated! The workflow file '${workflowMdFile}' has been modified more recently. Run 'gh aw compile' to regenerate the lock file.`;
|
||||
core.error(warningMessage);
|
||||
await core.summary
|
||||
.addRaw("## ⚠️ Workflow Lock File Warning\n\n")
|
||||
.addRaw(`🔴🔴🔴 **WARNING**: Lock file \`${lockFile}\` is outdated!\n\n`)
|
||||
.addRaw(`The workflow file \`${workflowMdFile}\` has been modified more recently.\n\n`)
|
||||
.addRaw("Run `gh aw compile` to regenerate the lock file.\n\n")
|
||||
.write();
|
||||
const workflowTimestamp = workflowStat.mtime.toISOString();
|
||||
const lockTimestamp = lockStat.mtime.toISOString();
|
||||
const gitSha = process.env.GITHUB_SHA;
|
||||
let summary = core.summary
|
||||
.addRaw("### ⚠️ Workflow Lock File Warning\n\n")
|
||||
.addRaw("**WARNING**: Lock file is outdated and needs to be regenerated.\n\n")
|
||||
.addRaw("**Files:**\n")
|
||||
.addRaw(`- Source: \`${workflowMdFile}\` (modified: ${workflowTimestamp})\n`)
|
||||
.addRaw(`- Lock: \`${lockFile}\` (modified: ${lockTimestamp})\n\n`);
|
||||
if (gitSha) {
|
||||
summary = summary.addRaw(`**Git Commit:** \`${gitSha}\`\n\n`);
|
||||
}
|
||||
summary = summary.addRaw("**Action Required:** Run `gh aw compile` to regenerate the lock file.\n\n");
|
||||
await summary.write();
|
||||
} else {
|
||||
core.info("✅ Lock file is up to date");
|
||||
}
|
||||
@@ -482,9 +500,7 @@ jobs:
|
||||
needs:
|
||||
- agent
|
||||
- detection
|
||||
if: >
|
||||
(((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment'))) &&
|
||||
(((github.event.issue.number) || (github.event.pull_request.number)) || (github.event.discussion.number))
|
||||
if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment'))
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -512,8 +528,8 @@ jobs:
|
||||
- name: Setup agent output environment variable
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/safeoutputs/
|
||||
find /tmp/gh-aw/safeoutputs/ -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> $GITHUB_ENV
|
||||
find "/tmp/gh-aw/safeoutputs/" -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Add Issue Comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
@@ -522,9 +538,44 @@ jobs:
|
||||
GH_AW_WORKFLOW_NAME: "Agentic Triage"
|
||||
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/issue-triage.md@0837fb7b24c3b84ee77fb7c8cfa8735c48be347a"
|
||||
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/0837fb7b24c3b84ee77fb7c8cfa8735c48be347a/workflows/issue-triage.md"
|
||||
GH_AW_COMMENT_TARGET: "*"
|
||||
with:
|
||||
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require("fs");
|
||||
function loadAgentOutput() {
|
||||
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
|
||||
if (!agentOutputFile) {
|
||||
core.info("No GH_AW_AGENT_OUTPUT environment variable found");
|
||||
return { success: false };
|
||||
}
|
||||
let outputContent;
|
||||
try {
|
||||
outputContent = fs.readFileSync(agentOutputFile, "utf8");
|
||||
} catch (error) {
|
||||
const errorMessage = `Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`;
|
||||
core.setFailed(errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
if (outputContent.trim() === "") {
|
||||
core.info("Agent output content is empty");
|
||||
return { success: false };
|
||||
}
|
||||
core.info(`Agent output content length: ${outputContent.length}`);
|
||||
let validatedOutput;
|
||||
try {
|
||||
validatedOutput = JSON.parse(outputContent);
|
||||
} catch (error) {
|
||||
const errorMessage = `Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`;
|
||||
core.setFailed(errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
if (!validatedOutput.items || !Array.isArray(validatedOutput.items)) {
|
||||
core.info("No valid items found in agent output");
|
||||
return { success: false };
|
||||
}
|
||||
return { success: true, items: validatedOutput.items };
|
||||
}
|
||||
function generateFooter(
|
||||
workflowName,
|
||||
runUrl,
|
||||
@@ -608,35 +659,11 @@ jobs:
|
||||
async function main() {
|
||||
const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true";
|
||||
const isDiscussionExplicit = process.env.GITHUB_AW_COMMENT_DISCUSSION === "true";
|
||||
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
|
||||
if (!agentOutputFile) {
|
||||
core.info("No GH_AW_AGENT_OUTPUT environment variable found");
|
||||
const result = loadAgentOutput();
|
||||
if (!result.success) {
|
||||
return;
|
||||
}
|
||||
let outputContent;
|
||||
try {
|
||||
outputContent = require("fs").readFileSync(agentOutputFile, "utf8");
|
||||
} catch (error) {
|
||||
core.setFailed(`Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return;
|
||||
}
|
||||
if (outputContent.trim() === "") {
|
||||
core.info("Agent output content is empty");
|
||||
return;
|
||||
}
|
||||
core.info(`Agent output content length: ${outputContent.length}`);
|
||||
let validatedOutput;
|
||||
try {
|
||||
validatedOutput = JSON.parse(outputContent);
|
||||
} catch (error) {
|
||||
core.setFailed(`Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return;
|
||||
}
|
||||
if (!validatedOutput.items || !Array.isArray(validatedOutput.items)) {
|
||||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
const commentItems = validatedOutput.items.filter( item => item.type === "add_comment");
|
||||
const commentItems = result.items.filter( item => item.type === "add_comment");
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
@@ -873,9 +900,7 @@ jobs:
|
||||
needs:
|
||||
- agent
|
||||
- detection
|
||||
if: >
|
||||
(((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels'))) &&
|
||||
((github.event.issue.number) || (github.event.pull_request.number))
|
||||
if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels'))
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -894,8 +919,8 @@ jobs:
|
||||
- name: Setup agent output environment variable
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/safeoutputs/
|
||||
find /tmp/gh-aw/safeoutputs/ -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> $GITHUB_ENV
|
||||
find "/tmp/gh-aw/safeoutputs/" -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Add Labels
|
||||
id: add_labels
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
@@ -903,6 +928,7 @@ jobs:
|
||||
GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
GH_AW_LABELS_ALLOWED: ""
|
||||
GH_AW_LABELS_MAX_COUNT: 5
|
||||
GH_AW_LABELS_TARGET: "*"
|
||||
with:
|
||||
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
@@ -911,8 +937,8 @@ jobs:
|
||||
return "";
|
||||
}
|
||||
let sanitized = content.trim();
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
sanitized = sanitized.replace(
|
||||
/(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
|
||||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
@@ -920,54 +946,86 @@ jobs:
|
||||
sanitized = sanitized.replace(/[<>&'"]/g, "");
|
||||
return sanitized.trim();
|
||||
}
|
||||
async function main() {
|
||||
const fs = require("fs");
|
||||
function loadAgentOutput() {
|
||||
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
|
||||
if (!agentOutputFile) {
|
||||
core.info("No GH_AW_AGENT_OUTPUT environment variable found");
|
||||
return;
|
||||
return { success: false };
|
||||
}
|
||||
let outputContent;
|
||||
try {
|
||||
outputContent = require("fs").readFileSync(agentOutputFile, "utf8");
|
||||
outputContent = fs.readFileSync(agentOutputFile, "utf8");
|
||||
} catch (error) {
|
||||
core.setFailed(`Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return;
|
||||
const errorMessage = `Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`;
|
||||
core.setFailed(errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
if (outputContent.trim() === "") {
|
||||
core.info("Agent output content is empty");
|
||||
return;
|
||||
return { success: false };
|
||||
}
|
||||
core.info(`Agent output content length: ${outputContent.length}`);
|
||||
let validatedOutput;
|
||||
try {
|
||||
validatedOutput = JSON.parse(outputContent);
|
||||
} catch (error) {
|
||||
core.setFailed(`Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return;
|
||||
const errorMessage = `Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`;
|
||||
core.setFailed(errorMessage);
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
if (!validatedOutput.items || !Array.isArray(validatedOutput.items)) {
|
||||
core.warning("No valid items found in agent output");
|
||||
core.info("No valid items found in agent output");
|
||||
return { success: false };
|
||||
}
|
||||
return { success: true, items: validatedOutput.items };
|
||||
}
|
||||
async function generateStagedPreview(options) {
|
||||
const { title, description, items, renderItem } = options;
|
||||
let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`;
|
||||
summaryContent += `${description}\n\n`;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
summaryContent += renderItem(item, i);
|
||||
summaryContent += "---\n\n";
|
||||
}
|
||||
try {
|
||||
await core.summary.addRaw(summaryContent).write();
|
||||
core.info(summaryContent);
|
||||
core.info(`📝 ${title} preview written to step summary`);
|
||||
} catch (error) {
|
||||
core.setFailed(error instanceof Error ? error : String(error));
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
const result = loadAgentOutput();
|
||||
if (!result.success) {
|
||||
return;
|
||||
}
|
||||
const labelsItem = validatedOutput.items.find(item => item.type === "add_labels");
|
||||
const labelsItem = result.items.find(item => item.type === "add_labels");
|
||||
if (!labelsItem) {
|
||||
core.warning("No add-labels item found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found add-labels item with ${labelsItem.labels.length} labels`);
|
||||
if (process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true") {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Labels Preview\n\n";
|
||||
summaryContent += "The following labels would be added if staged mode was disabled:\n\n";
|
||||
if (labelsItem.item_number) {
|
||||
summaryContent += `**Target Issue:** #${labelsItem.item_number}\n\n`;
|
||||
} else {
|
||||
summaryContent += `**Target:** Current issue/PR\n\n`;
|
||||
}
|
||||
if (labelsItem.labels && labelsItem.labels.length > 0) {
|
||||
summaryContent += `**Labels to add:** ${labelsItem.labels.join(", ")}\n\n`;
|
||||
}
|
||||
await core.summary.addRaw(summaryContent).write();
|
||||
core.info("📝 Label addition preview written to step summary");
|
||||
await generateStagedPreview({
|
||||
title: "Add Labels",
|
||||
description: "The following labels would be added if staged mode was disabled:",
|
||||
items: [labelsItem],
|
||||
renderItem: item => {
|
||||
let content = "";
|
||||
if (item.item_number) {
|
||||
content += `**Target Issue:** #${item.item_number}\n\n`;
|
||||
} else {
|
||||
content += `**Target:** Current issue/PR\n\n`;
|
||||
}
|
||||
if (item.labels && item.labels.length > 0) {
|
||||
content += `**Labels to add:** ${item.labels.join(", ")}\n\n`;
|
||||
}
|
||||
return content;
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
const allowedLabelsEnv = process.env.GH_AW_LABELS_ALLOWED?.trim();
|
||||
@@ -1119,7 +1177,6 @@ jobs:
|
||||
group: "gh-aw-copilot-${{ github.workflow }}"
|
||||
env:
|
||||
GH_AW_SAFE_OUTPUTS: /tmp/gh-aw/safeoutputs/outputs.jsonl
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"max\":5},\"missing_tool\":{}}"
|
||||
outputs:
|
||||
output: ${{ steps.collect_output.outputs.output }}
|
||||
output_types: ${{ steps.collect_output.outputs.output_types }}
|
||||
@@ -1180,24 +1237,29 @@ jobs:
|
||||
main().catch(error => {
|
||||
core.setFailed(error instanceof Error ? error.message : String(error));
|
||||
});
|
||||
- name: Validate COPILOT_CLI_TOKEN secret
|
||||
- name: Validate COPILOT_GITHUB_TOKEN or COPILOT_CLI_TOKEN secret
|
||||
run: |
|
||||
if [ -z "$COPILOT_CLI_TOKEN" ]; then
|
||||
echo "Error: COPILOT_CLI_TOKEN secret is not set"
|
||||
echo "The GitHub Copilot CLI engine requires the COPILOT_CLI_TOKEN secret to be configured."
|
||||
echo "Please configure this secret in your repository settings."
|
||||
if [ -z "$COPILOT_GITHUB_TOKEN" ] && [ -z "$COPILOT_CLI_TOKEN" ]; then
|
||||
echo "Error: Neither COPILOT_GITHUB_TOKEN nor COPILOT_CLI_TOKEN secret is set"
|
||||
echo "The GitHub Copilot CLI engine requires either COPILOT_GITHUB_TOKEN or COPILOT_CLI_TOKEN secret to be configured."
|
||||
echo "Please configure one of these secrets in your repository settings."
|
||||
echo "Documentation: https://githubnext.github.io/gh-aw/reference/engines/#github-copilot-default"
|
||||
exit 1
|
||||
fi
|
||||
echo "COPILOT_CLI_TOKEN secret is configured"
|
||||
if [ -n "$COPILOT_GITHUB_TOKEN" ]; then
|
||||
echo "COPILOT_GITHUB_TOKEN secret is configured"
|
||||
else
|
||||
echo "COPILOT_CLI_TOKEN secret is configured (using as fallback for COPILOT_GITHUB_TOKEN)"
|
||||
fi
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903
|
||||
with:
|
||||
node-version: '24'
|
||||
- name: Install GitHub Copilot CLI
|
||||
run: npm install -g @github/copilot@0.0.353
|
||||
run: npm install -g @github/copilot@0.0.354
|
||||
- name: Downloading container images
|
||||
run: |
|
||||
set -e
|
||||
@@ -1207,7 +1269,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/safeoutputs
|
||||
cat > /tmp/gh-aw/safeoutputs/config.json << 'EOF'
|
||||
{"add_comment":{"max":1},"add_labels":{"max":5},"missing_tool":{}}
|
||||
{"add_comment":{"max":1,"target":"*"},"add_labels":{"max":5},"missing_tool":{}}
|
||||
EOF
|
||||
cat > /tmp/gh-aw/safeoutputs/mcp-server.cjs << 'EOF'
|
||||
const fs = require("fs");
|
||||
@@ -1231,39 +1293,26 @@ jobs:
|
||||
normalized = normalized.toLowerCase();
|
||||
return normalized;
|
||||
}
|
||||
const configEnv = process.env.GH_AW_SAFE_OUTPUTS_CONFIG;
|
||||
const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json";
|
||||
let safeOutputsConfigRaw;
|
||||
if (!configEnv) {
|
||||
const defaultConfigPath = "/tmp/gh-aw/safeoutputs/config.json";
|
||||
debug(`GH_AW_SAFE_OUTPUTS_CONFIG not set, attempting to read from default path: ${defaultConfigPath}`);
|
||||
try {
|
||||
if (fs.existsSync(defaultConfigPath)) {
|
||||
debug(`Reading config from file: ${defaultConfigPath}`);
|
||||
const configFileContent = fs.readFileSync(defaultConfigPath, "utf8");
|
||||
debug(`Config file content length: ${configFileContent.length} characters`);
|
||||
debug(`Config file read successfully, attempting to parse JSON`);
|
||||
safeOutputsConfigRaw = JSON.parse(configFileContent);
|
||||
debug(`Successfully parsed config from file with ${Object.keys(safeOutputsConfigRaw).length} configuration keys`);
|
||||
} else {
|
||||
debug(`Config file does not exist at: ${defaultConfigPath}`);
|
||||
debug(`Using minimal default configuration`);
|
||||
safeOutputsConfigRaw = {};
|
||||
}
|
||||
} catch (error) {
|
||||
debug(`Error reading config file: ${error instanceof Error ? error.message : String(error)}`);
|
||||
debug(`Falling back to empty configuration`);
|
||||
debug(`Reading config from file: ${configPath}`);
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
debug(`Config file exists at: ${configPath}`);
|
||||
const configFileContent = fs.readFileSync(configPath, "utf8");
|
||||
debug(`Config file content length: ${configFileContent.length} characters`);
|
||||
debug(`Config file read successfully, attempting to parse JSON`);
|
||||
safeOutputsConfigRaw = JSON.parse(configFileContent);
|
||||
debug(`Successfully parsed config from file with ${Object.keys(safeOutputsConfigRaw).length} configuration keys`);
|
||||
} else {
|
||||
debug(`Config file does not exist at: ${configPath}`);
|
||||
debug(`Using minimal default configuration`);
|
||||
safeOutputsConfigRaw = {};
|
||||
}
|
||||
} else {
|
||||
debug(`Using GH_AW_SAFE_OUTPUTS_CONFIG from environment variable`);
|
||||
debug(`Config environment variable length: ${configEnv.length} characters`);
|
||||
try {
|
||||
safeOutputsConfigRaw = JSON.parse(configEnv);
|
||||
debug(`Successfully parsed config from environment: ${JSON.stringify(safeOutputsConfigRaw)}`);
|
||||
} catch (error) {
|
||||
debug(`Error parsing config from environment: ${error instanceof Error ? error.message : String(error)}`);
|
||||
throw new Error(`Failed to parse GH_AW_SAFE_OUTPUTS_CONFIG: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
} catch (error) {
|
||||
debug(`Error reading config file: ${error instanceof Error ? error.message : String(error)}`);
|
||||
debug(`Falling back to empty configuration`);
|
||||
safeOutputsConfigRaw = {};
|
||||
}
|
||||
const safeOutputsConfig = Object.fromEntries(Object.entries(safeOutputsConfigRaw).map(([k, v]) => [k.replace(/-/g, "_"), v]));
|
||||
debug(`Final processed config: ${JSON.stringify(safeOutputsConfig)}`);
|
||||
@@ -1509,6 +1558,17 @@ jobs:
|
||||
};
|
||||
};
|
||||
function getCurrentBranch() {
|
||||
const cwd = process.env.GITHUB_WORKSPACE || process.cwd();
|
||||
try {
|
||||
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
||||
encoding: "utf8",
|
||||
cwd: cwd,
|
||||
}).trim();
|
||||
debug(`Resolved current branch from git in ${cwd}: ${branch}`);
|
||||
return branch;
|
||||
} catch (error) {
|
||||
debug(`Failed to get branch from git: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
const ghHeadRef = process.env.GITHUB_HEAD_REF;
|
||||
const ghRefName = process.env.GITHUB_REF_NAME;
|
||||
if (ghHeadRef) {
|
||||
@@ -1519,23 +1579,22 @@ jobs:
|
||||
debug(`Resolved current branch from GITHUB_REF_NAME: ${ghRefName}`);
|
||||
return ghRefName;
|
||||
}
|
||||
const cwd = process.env.GITHUB_WORKSPACE || process.cwd();
|
||||
try {
|
||||
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
||||
encoding: "utf8",
|
||||
cwd: cwd,
|
||||
}).trim();
|
||||
debug(`Resolved current branch from git in ${cwd}: ${branch}`);
|
||||
return branch;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get current branch: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
throw new Error("Failed to determine current branch: git command failed and no GitHub environment variables available");
|
||||
}
|
||||
function getBaseBranch() {
|
||||
return process.env.GH_AW_BASE_BRANCH || "main";
|
||||
}
|
||||
const createPullRequestHandler = args => {
|
||||
const entry = { ...args, type: "create_pull_request" };
|
||||
if (!entry.branch || entry.branch.trim() === "") {
|
||||
entry.branch = getCurrentBranch();
|
||||
debug(`Using current branch for create_pull_request: ${entry.branch}`);
|
||||
const baseBranch = getBaseBranch();
|
||||
if (!entry.branch || entry.branch.trim() === "" || entry.branch === baseBranch) {
|
||||
const detectedBranch = getCurrentBranch();
|
||||
if (entry.branch === baseBranch) {
|
||||
debug(`Branch equals base branch (${baseBranch}), detecting actual working branch: ${detectedBranch}`);
|
||||
} else {
|
||||
debug(`Using current branch for create_pull_request: ${detectedBranch}`);
|
||||
}
|
||||
entry.branch = detectedBranch;
|
||||
}
|
||||
appendSafeOutput(entry);
|
||||
return {
|
||||
@@ -1549,9 +1608,15 @@ jobs:
|
||||
};
|
||||
const pushToPullRequestBranchHandler = args => {
|
||||
const entry = { ...args, type: "push_to_pull_request_branch" };
|
||||
if (!entry.branch || entry.branch.trim() === "") {
|
||||
entry.branch = getCurrentBranch();
|
||||
debug(`Using current branch for push_to_pull_request_branch: ${entry.branch}`);
|
||||
const baseBranch = getBaseBranch();
|
||||
if (!entry.branch || entry.branch.trim() === "" || entry.branch === baseBranch) {
|
||||
const detectedBranch = getCurrentBranch();
|
||||
if (entry.branch === baseBranch) {
|
||||
debug(`Branch equals base branch (${baseBranch}), detecting actual working branch: ${detectedBranch}`);
|
||||
} else {
|
||||
debug(`Using current branch for push_to_pull_request_branch: ${detectedBranch}`);
|
||||
}
|
||||
entry.branch = detectedBranch;
|
||||
}
|
||||
appendSafeOutput(entry);
|
||||
return {
|
||||
@@ -1995,7 +2060,6 @@ jobs:
|
||||
env:
|
||||
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG: ${{ toJSON(env.GH_AW_SAFE_OUTPUTS_CONFIG) }}
|
||||
GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }}
|
||||
GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }}
|
||||
GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }}
|
||||
@@ -2032,7 +2096,6 @@ jobs:
|
||||
"tools": ["*"],
|
||||
"env": {
|
||||
"GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}",
|
||||
"GH_AW_SAFE_OUTPUTS_CONFIG": "\${GH_AW_SAFE_OUTPUTS_CONFIG}",
|
||||
"GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}",
|
||||
"GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}",
|
||||
"GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}",
|
||||
@@ -2065,8 +2128,9 @@ jobs:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
run: |
|
||||
mkdir -p $(dirname "$GH_AW_PROMPT")
|
||||
cat > "$GH_AW_PROMPT" << 'PROMPT_EOF'
|
||||
PROMPT_DIR="$(dirname "$GH_AW_PROMPT")"
|
||||
mkdir -p "$PROMPT_DIR"
|
||||
cat > "$GH_AW_PROMPT" << PROMPT_EOF
|
||||
# Agentic Triage
|
||||
|
||||
|
||||
@@ -2134,7 +2198,7 @@ jobs:
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
run: |
|
||||
cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF'
|
||||
cat >> "$GH_AW_PROMPT" << PROMPT_EOF
|
||||
|
||||
---
|
||||
|
||||
@@ -2166,7 +2230,7 @@ jobs:
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
run: |
|
||||
cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF'
|
||||
cat >> "$GH_AW_PROMPT" << PROMPT_EOF
|
||||
|
||||
---
|
||||
|
||||
@@ -2179,7 +2243,7 @@ jobs:
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
run: |
|
||||
cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF'
|
||||
cat >> "$GH_AW_PROMPT" << PROMPT_EOF
|
||||
|
||||
---
|
||||
|
||||
@@ -2204,7 +2268,7 @@ jobs:
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
run: |
|
||||
cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF'
|
||||
cat >> "$GH_AW_PROMPT" << PROMPT_EOF
|
||||
|
||||
---
|
||||
|
||||
@@ -2269,21 +2333,27 @@ jobs:
|
||||
}
|
||||
}
|
||||
main();
|
||||
- name: Print prompt to step summary
|
||||
- name: Print prompt
|
||||
env:
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
run: |
|
||||
echo "<details>" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "<summary>Generated Prompt</summary>" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '```markdown' >> "$GITHUB_STEP_SUMMARY"
|
||||
cat "$GH_AW_PROMPT" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "</details>" >> "$GITHUB_STEP_SUMMARY"
|
||||
# Print prompt to workflow logs (equivalent to core.info)
|
||||
echo "Generated Prompt:"
|
||||
cat "$GH_AW_PROMPT"
|
||||
# Print prompt to step summary
|
||||
{
|
||||
echo "<details>"
|
||||
echo "<summary>Generated Prompt</summary>"
|
||||
echo ""
|
||||
echo '```markdown'
|
||||
cat "$GH_AW_PROMPT"
|
||||
echo '```'
|
||||
echo ""
|
||||
echo "</details>"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: Upload prompt
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: prompt.txt
|
||||
path: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
@@ -2299,7 +2369,7 @@ jobs:
|
||||
engine_name: "GitHub Copilot CLI",
|
||||
model: "",
|
||||
version: "",
|
||||
agent_version: "0.0.353",
|
||||
agent_version: "0.0.354",
|
||||
workflow_name: "Agentic Triage",
|
||||
experimental: false,
|
||||
supports_tools_allowlist: true,
|
||||
@@ -2326,7 +2396,7 @@ jobs:
|
||||
console.log(JSON.stringify(awInfo, null, 2));
|
||||
- name: Upload agentic run info
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: aw_info.json
|
||||
path: /tmp/gh-aw/aw_info.json
|
||||
@@ -2340,7 +2410,7 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
set -o pipefail
|
||||
COPILOT_CLI_INSTRUCTION=$(cat /tmp/gh-aw/aw-prompts/prompt.txt)
|
||||
COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"
|
||||
mkdir -p /tmp/
|
||||
mkdir -p /tmp/gh-aw/
|
||||
mkdir -p /tmp/gh-aw/agent/
|
||||
@@ -2348,15 +2418,14 @@ jobs:
|
||||
copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/.copilot/logs/ --disable-builtin-mcps --allow-tool github --allow-tool safeoutputs --allow-tool web-fetch --prompt "$COPILOT_CLI_INSTRUCTION" 2>&1 | tee /tmp/gh-aw/agent-stdio.log
|
||||
env:
|
||||
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN || secrets.COPILOT_CLI_TOKEN }}
|
||||
GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"max\":5},\"missing_tool\":{}}"
|
||||
GITHUB_HEAD_REF: ${{ github.head_ref }}
|
||||
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||
GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }}
|
||||
GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }}
|
||||
GITHUB_WORKSPACE: ${{ github.workspace }}
|
||||
XDG_CONFIG_HOME: /home/runner
|
||||
- name: Redact secrets in logs
|
||||
@@ -2470,13 +2539,14 @@ jobs:
|
||||
}
|
||||
await main();
|
||||
env:
|
||||
GH_AW_SECRET_NAMES: 'COPILOT_CLI_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
|
||||
GH_AW_SECRET_NAMES: 'COPILOT_CLI_TOKEN,COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
|
||||
SECRET_COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }}
|
||||
SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
|
||||
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload Safe Outputs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: safe_output.jsonl
|
||||
path: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
@@ -2486,24 +2556,58 @@ jobs:
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
env:
|
||||
GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"max\":5},\"missing_tool\":{}}"
|
||||
GH_AW_ALLOWED_DOMAINS: "api.enterprise.githubcopilot.com,api.github.com,github.com,raw.githubusercontent.com,registry.npmjs.org"
|
||||
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||
GITHUB_API_URL: ${{ github.api_url }}
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
const fs = require("fs");
|
||||
function extractDomainsFromUrl(url) {
|
||||
if (!url || typeof url !== "string") {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const hostname = urlObj.hostname.toLowerCase();
|
||||
const domains = [hostname];
|
||||
if (hostname === "github.com") {
|
||||
domains.push("api.github.com");
|
||||
domains.push("raw.githubusercontent.com");
|
||||
domains.push("*.githubusercontent.com");
|
||||
}
|
||||
else if (!hostname.startsWith("api.")) {
|
||||
domains.push("api." + hostname);
|
||||
domains.push("raw." + hostname);
|
||||
}
|
||||
return domains;
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
function sanitizeContent(content, maxLength) {
|
||||
if (!content || typeof content !== "string") {
|
||||
return "";
|
||||
}
|
||||
const allowedDomainsEnv = process.env.GH_AW_ALLOWED_DOMAINS;
|
||||
const defaultAllowedDomains = ["github.com", "github.io", "githubusercontent.com", "githubassets.com", "github.dev", "codespaces.new"];
|
||||
const allowedDomains = allowedDomainsEnv
|
||||
let allowedDomains = allowedDomainsEnv
|
||||
? allowedDomainsEnv
|
||||
.split(",")
|
||||
.map(d => d.trim())
|
||||
.filter(d => d)
|
||||
: defaultAllowedDomains;
|
||||
const githubServerUrl = process.env.GITHUB_SERVER_URL;
|
||||
const githubApiUrl = process.env.GITHUB_API_URL;
|
||||
if (githubServerUrl) {
|
||||
const serverDomains = extractDomainsFromUrl(githubServerUrl);
|
||||
allowedDomains = allowedDomains.concat(serverDomains);
|
||||
}
|
||||
if (githubApiUrl) {
|
||||
const apiDomains = extractDomainsFromUrl(githubApiUrl);
|
||||
allowedDomains = allowedDomains.concat(apiDomains);
|
||||
}
|
||||
allowedDomains = [...new Set(allowedDomains)];
|
||||
let sanitized = content;
|
||||
sanitized = neutralizeCommands(sanitized);
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
@@ -2918,7 +3022,16 @@ jobs:
|
||||
}
|
||||
}
|
||||
const outputFile = process.env.GH_AW_SAFE_OUTPUTS;
|
||||
const safeOutputsConfig = process.env.GH_AW_SAFE_OUTPUTS_CONFIG;
|
||||
const configPath = process.env.GH_AW_SAFE_OUTPUTS_CONFIG_PATH || "/tmp/gh-aw/safeoutputs/config.json";
|
||||
let safeOutputsConfig;
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const configFileContent = fs.readFileSync(configPath, "utf8");
|
||||
safeOutputsConfig = JSON.parse(configFileContent);
|
||||
}
|
||||
} catch (error) {
|
||||
core.warning(`Failed to read config file from ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
if (!outputFile) {
|
||||
core.info("GH_AW_SAFE_OUTPUTS not set, no output to collect");
|
||||
core.setOutput("output", "");
|
||||
@@ -2937,8 +3050,7 @@ jobs:
|
||||
let expectedOutputTypes = {};
|
||||
if (safeOutputsConfig) {
|
||||
try {
|
||||
const rawConfig = JSON.parse(safeOutputsConfig);
|
||||
expectedOutputTypes = Object.fromEntries(Object.entries(rawConfig).map(([key, value]) => [key.replace(/-/g, "_"), value]));
|
||||
expectedOutputTypes = Object.fromEntries(Object.entries(safeOutputsConfig).map(([key, value]) => [key.replace(/-/g, "_"), value]));
|
||||
core.info(`Expected output types: ${JSON.stringify(Object.keys(expectedOutputTypes))}`);
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
@@ -3309,13 +3421,13 @@ jobs:
|
||||
await main();
|
||||
- name: Upload sanitized agent output
|
||||
if: always() && env.GH_AW_AGENT_OUTPUT
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: agent_output.json
|
||||
path: ${{ env.GH_AW_AGENT_OUTPUT }}
|
||||
if-no-files-found: warn
|
||||
- name: Upload engine output files
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: agent_outputs
|
||||
path: |
|
||||
@@ -3323,7 +3435,7 @@ jobs:
|
||||
if-no-files-found: ignore
|
||||
- name: Upload MCP logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: mcp-logs
|
||||
path: /tmp/gh-aw/mcp-logs/
|
||||
@@ -4208,7 +4320,7 @@ jobs:
|
||||
main();
|
||||
- name: Upload Agent Stdio
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: agent-stdio.log
|
||||
path: /tmp/gh-aw/agent-stdio.log
|
||||
@@ -4590,24 +4702,29 @@ jobs:
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/threat-detection
|
||||
touch /tmp/gh-aw/threat-detection/detection.log
|
||||
- name: Validate COPILOT_CLI_TOKEN secret
|
||||
- name: Validate COPILOT_GITHUB_TOKEN or COPILOT_CLI_TOKEN secret
|
||||
run: |
|
||||
if [ -z "$COPILOT_CLI_TOKEN" ]; then
|
||||
echo "Error: COPILOT_CLI_TOKEN secret is not set"
|
||||
echo "The GitHub Copilot CLI engine requires the COPILOT_CLI_TOKEN secret to be configured."
|
||||
echo "Please configure this secret in your repository settings."
|
||||
if [ -z "$COPILOT_GITHUB_TOKEN" ] && [ -z "$COPILOT_CLI_TOKEN" ]; then
|
||||
echo "Error: Neither COPILOT_GITHUB_TOKEN nor COPILOT_CLI_TOKEN secret is set"
|
||||
echo "The GitHub Copilot CLI engine requires either COPILOT_GITHUB_TOKEN or COPILOT_CLI_TOKEN secret to be configured."
|
||||
echo "Please configure one of these secrets in your repository settings."
|
||||
echo "Documentation: https://githubnext.github.io/gh-aw/reference/engines/#github-copilot-default"
|
||||
exit 1
|
||||
fi
|
||||
echo "COPILOT_CLI_TOKEN secret is configured"
|
||||
if [ -n "$COPILOT_GITHUB_TOKEN" ]; then
|
||||
echo "COPILOT_GITHUB_TOKEN secret is configured"
|
||||
else
|
||||
echo "COPILOT_CLI_TOKEN secret is configured (using as fallback for COPILOT_GITHUB_TOKEN)"
|
||||
fi
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903
|
||||
with:
|
||||
node-version: '24'
|
||||
- name: Install GitHub Copilot CLI
|
||||
run: npm install -g @github/copilot@0.0.353
|
||||
run: npm install -g @github/copilot@0.0.354
|
||||
- name: Execute GitHub Copilot CLI
|
||||
id: agentic_execution
|
||||
# Copilot CLI tool arguments (sorted):
|
||||
@@ -4621,7 +4738,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
set -o pipefail
|
||||
COPILOT_CLI_INSTRUCTION=$(cat /tmp/gh-aw/aw-prompts/prompt.txt)
|
||||
COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"
|
||||
mkdir -p /tmp/
|
||||
mkdir -p /tmp/gh-aw/
|
||||
mkdir -p /tmp/gh-aw/agent/
|
||||
@@ -4629,11 +4746,11 @@ jobs:
|
||||
copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/.copilot/logs/ --disable-builtin-mcps --allow-tool 'shell(cat)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(jq)' --allow-tool 'shell(ls)' --allow-tool 'shell(tail)' --allow-tool 'shell(wc)' --prompt "$COPILOT_CLI_INSTRUCTION" 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log
|
||||
env:
|
||||
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN || secrets.COPILOT_CLI_TOKEN }}
|
||||
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
|
||||
GITHUB_HEAD_REF: ${{ github.head_ref }}
|
||||
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||
GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }}
|
||||
GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }}
|
||||
GITHUB_WORKSPACE: ${{ github.workspace }}
|
||||
XDG_CONFIG_HOME: /home/runner
|
||||
- name: Parse threat detection results
|
||||
@@ -4674,7 +4791,7 @@ jobs:
|
||||
}
|
||||
- name: Upload threat detection log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: threat-detection.log
|
||||
path: /tmp/gh-aw/threat-detection/detection.log
|
||||
@@ -4702,8 +4819,8 @@ jobs:
|
||||
- name: Setup agent output environment variable
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/safeoutputs/
|
||||
find /tmp/gh-aw/safeoutputs/ -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> $GITHUB_ENV
|
||||
find "/tmp/gh-aw/safeoutputs/" -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Record Missing Tool
|
||||
id: missing_tool
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
@@ -4821,7 +4938,7 @@ jobs:
|
||||
id: check_stop_time
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
env:
|
||||
GH_AW_STOP_TIME: 2025-12-03 20:01:19
|
||||
GH_AW_STOP_TIME: 2025-12-06 19:47:58
|
||||
GH_AW_WORKFLOW_NAME: "Agentic Triage"
|
||||
with:
|
||||
script: |
|
||||
@@ -4891,8 +5008,8 @@ jobs:
|
||||
- name: Setup agent output environment variable
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/safeoutputs/
|
||||
find /tmp/gh-aw/safeoutputs/ -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> $GITHUB_ENV
|
||||
find "/tmp/gh-aw/safeoutputs/" -type f -print
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
|
||||
- name: Update reaction comment with completion status
|
||||
id: update_reaction
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
|
||||
|
||||
@@ -8,12 +8,25 @@ on:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
# Add stricter error-detection patterns so issue text doesn't trigger agent error detection.
|
||||
# After merging this file run `gh aw compile` to regenerate the lock file.
|
||||
env:
|
||||
GH_AW_ERROR_PATTERNS: >-
|
||||
[
|
||||
{"id":"gh-action-error","pattern":"^::(error)(?:\\\\s+[^:]*)?::(.+)","level_group":1,"message_group":2,"description":"GitHub Actions workflow command - error"},
|
||||
{"id":"gh-action-warning","pattern":"^::(warning)(?:\\\\s+[^:]*)?::(.+)","level_group":1,"message_group":2,"description":"GitHub Actions workflow command - warning"},
|
||||
{"id":"bracketed-level","pattern":"^\\[(ERROR|CRITICAL|WARNING|WARN)\\]\\s+(.+)","level_group":1,"message_group":2,"description":"Bracketed log level at start of line"},
|
||||
{"id":"timestamped-copilot","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z\\s+\\[(ERROR|WARN|WARNING|CRITICAL)\\]\\s+(.+)","level_group":1,"message_group":3,"description":"Timestamped Copilot CLI messages"}
|
||||
]
|
||||
|
||||
network: defaults
|
||||
|
||||
safe-outputs:
|
||||
add-labels:
|
||||
max: 5
|
||||
target: "*"
|
||||
add-comment:
|
||||
target: "*"
|
||||
|
||||
tools:
|
||||
web-fetch:
|
||||
|
||||
Reference in New Issue
Block a user