mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
fix: bind health reports to canonical metadata
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5b21ed1018fa0f8f4e8f957e2dd4013afc769179aa0a99ff6d2b09a7c4a00f5d","body_hash":"d8be381faa0bbe39cfb3cd752ee9642e9a548ebccceb469d9f60780707749c67","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","agent_model":"${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'gpt-5.6-sol' }}","engine_versions":{"copilot":"1.0.80"}}
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5b21ed1018fa0f8f4e8f957e2dd4013afc769179aa0a99ff6d2b09a7c4a00f5d","body_hash":"5f0e69633dc01f193c3150ee4aebe4c38f5280178f7fcedd09cc5283e0af3597","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","agent_model":"${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'gpt-5.6-sol' }}","engine_versions":{"copilot":"1.0.80"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"5e508589e03a7757a7e05b26e834292f5445bfb6","version":"v0.88.7"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14","digest":"sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14","digest":"sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14","digest":"sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.18","digest":"sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:33e1ec1d967ac1f28c2cedc24ce103dea3226840626de345d3fe579e96cf5c7d","pinned_image":"ghcr.io/github/gh-aw-node@sha256:33e1ec1d967ac1f28c2cedc24ce103dea3226840626de345d3fe579e96cf5c7d"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"mcp_servers":[{"name":"github","tools":["actions_get","actions_list","get_commit","get_file_contents","get_job_logs","get_latest_release","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_releases","list_starred_repositories","list_tags","search_code","search_issues","search_repositories"]},{"name":"safeoutputs","tools":["missing_data","missing_tool","noop","publish_groomed_dashboard"]}]}
|
||||
# This file was automatically generated by gh-aw (v0.88.7). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
|
||||
@@ -472,7 +472,7 @@ engine:
|
||||
COPILOT_GITHUB_TOKEN: ${{ case(needs.pat_pool.outputs.pat_number == '0', secrets.COPILOT_PAT_0, needs.pat_pool.outputs.pat_number == '1', secrets.COPILOT_PAT_1, needs.pat_pool.outputs.pat_number == '2', secrets.COPILOT_PAT_2, needs.pat_pool.outputs.pat_number == '3', secrets.COPILOT_PAT_3, needs.pat_pool.outputs.pat_number == '4', secrets.COPILOT_PAT_4, needs.pat_pool.outputs.pat_number == '5', secrets.COPILOT_PAT_5, needs.pat_pool.outputs.pat_number == '6', secrets.COPILOT_PAT_6, needs.pat_pool.outputs.pat_number == '7', secrets.COPILOT_PAT_7, needs.pat_pool.outputs.pat_number == '8', secrets.COPILOT_PAT_8, needs.pat_pool.outputs.pat_number == '9', secrets.COPILOT_PAT_9, 'NO COPILOT PAT AVAILABLE') }}
|
||||
---
|
||||
|
||||
# DevOps Health — Groom Dashboard
|
||||
## DevOps Health — Groom Dashboard
|
||||
|
||||
You are a dashboard grooming agent. You run after the daily health check and its dispatched investigations have had time to complete. Your job is to:
|
||||
|
||||
|
||||
+34
-12
@@ -1,4 +1,4 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e24998ddf2fa1a9beb33c6f72348c6d92cb34ba1a3337581f1ae816f08a1f4bd","body_hash":"9c6b1f5a55f7328496bfea9d9e1068450c69087ee06c00ee468aed247f87837a","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","agent_model":"${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'gpt-5.6-sol' }}","engine_versions":{"copilot":"1.0.80"}}
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e34f48cc8fc1495672f03aeab4cdf58d59c0aac68d6341aad00799432b2c4e7a","body_hash":"9c6b1f5a55f7328496bfea9d9e1068450c69087ee06c00ee468aed247f87837a","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","agent_model":"${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'gpt-5.6-sol' }}","engine_versions":{"copilot":"1.0.80"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"5e508589e03a7757a7e05b26e834292f5445bfb6","version":"v0.88.7"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14","digest":"sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14","digest":"sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14","digest":"sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.18","digest":"sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:33e1ec1d967ac1f28c2cedc24ce103dea3226840626de345d3fe579e96cf5c7d","pinned_image":"ghcr.io/github/gh-aw-node@sha256:33e1ec1d967ac1f28c2cedc24ce103dea3226840626de345d3fe579e96cf5c7d"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"mcp_servers":[{"name":"github","tools":["actions_get","actions_list","get_commit","get_file_contents","get_job_logs","get_latest_release","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["missing_data","missing_tool","noop","publish_investigation_report"]}]}
|
||||
# This file was automatically generated by gh-aw (v0.88.7). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
@@ -2112,15 +2112,6 @@ jobs:
|
||||
throw new Error("Dashboard history schema is invalid");
|
||||
}
|
||||
}
|
||||
const activeFinding = stateFindings.get(findingId);
|
||||
if (
|
||||
activeFinding &&
|
||||
activeFinding.severity !== expectedSeverity
|
||||
) {
|
||||
throw new Error(
|
||||
"Active finding severity does not match workflow input"
|
||||
);
|
||||
}
|
||||
const escapedFindingId = findingId.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
@@ -2130,16 +2121,47 @@ jobs:
|
||||
"\\$&"
|
||||
);
|
||||
const pendingRowPattern = new RegExp(
|
||||
`^\\| \`${escapedFindingId}\` \\| [^|]* \\| [^|]* ` +
|
||||
`^\\| \`${escapedFindingId}\` \\| ([^|]*) \\| ([^|]*) ` +
|
||||
`\\| ⏳ Pending \\| [^|]* \\| [^\\r\\n]*` +
|
||||
`<!-- correlation:${escapedCorrelationId} --> [^\\r\\n]*\\|$`,
|
||||
"m"
|
||||
);
|
||||
if (!pendingRowPattern.test(issue.body || "")) {
|
||||
const pendingRow = (issue.body || "").match(pendingRowPattern);
|
||||
if (!pendingRow) {
|
||||
throw new Error(
|
||||
"Finding and correlation are not an active pending dashboard row"
|
||||
);
|
||||
}
|
||||
const rowTitle = pendingRow[1].trim();
|
||||
const severityByLabel = {
|
||||
"🔴 Critical": "critical",
|
||||
"🟡 Warning": "warning",
|
||||
"🔵 Info": "info",
|
||||
};
|
||||
const rowSeverity = severityByLabel[pendingRow[2].trim()];
|
||||
const activeFinding = stateFindings.get(findingId);
|
||||
if (
|
||||
!rowSeverity ||
|
||||
expectedSeverity !== rowSeverity ||
|
||||
!reportBody.startsWith(`## 🔍 Investigation: ${rowTitle}\n`) ||
|
||||
!reportBody.match(
|
||||
new RegExp(
|
||||
`^\\*\\*Severity:\\*\\* ${rowSeverity}\\s*$`,
|
||||
"m"
|
||||
)
|
||||
) ||
|
||||
(
|
||||
activeFinding &&
|
||||
(
|
||||
activeFinding.title !== rowTitle ||
|
||||
activeFinding.severity !== rowSeverity
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
"Investigation report title or severity does not match the pending row"
|
||||
);
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: issueNumber,
|
||||
|
||||
@@ -390,15 +390,6 @@ safe-outputs:
|
||||
throw new Error("Dashboard history schema is invalid");
|
||||
}
|
||||
}
|
||||
const activeFinding = stateFindings.get(findingId);
|
||||
if (
|
||||
activeFinding &&
|
||||
activeFinding.severity !== expectedSeverity
|
||||
) {
|
||||
throw new Error(
|
||||
"Active finding severity does not match workflow input"
|
||||
);
|
||||
}
|
||||
const escapedFindingId = findingId.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
@@ -408,16 +399,47 @@ safe-outputs:
|
||||
"\\$&"
|
||||
);
|
||||
const pendingRowPattern = new RegExp(
|
||||
`^\\| \`${escapedFindingId}\` \\| [^|]* \\| [^|]* ` +
|
||||
`^\\| \`${escapedFindingId}\` \\| ([^|]*) \\| ([^|]*) ` +
|
||||
`\\| ⏳ Pending \\| [^|]* \\| [^\\r\\n]*` +
|
||||
`<!-- correlation:${escapedCorrelationId} --> [^\\r\\n]*\\|$`,
|
||||
"m"
|
||||
);
|
||||
if (!pendingRowPattern.test(issue.body || "")) {
|
||||
const pendingRow = (issue.body || "").match(pendingRowPattern);
|
||||
if (!pendingRow) {
|
||||
throw new Error(
|
||||
"Finding and correlation are not an active pending dashboard row"
|
||||
);
|
||||
}
|
||||
const rowTitle = pendingRow[1].trim();
|
||||
const severityByLabel = {
|
||||
"🔴 Critical": "critical",
|
||||
"🟡 Warning": "warning",
|
||||
"🔵 Info": "info",
|
||||
};
|
||||
const rowSeverity = severityByLabel[pendingRow[2].trim()];
|
||||
const activeFinding = stateFindings.get(findingId);
|
||||
if (
|
||||
!rowSeverity ||
|
||||
expectedSeverity !== rowSeverity ||
|
||||
!reportBody.startsWith(`## 🔍 Investigation: ${rowTitle}\n`) ||
|
||||
!reportBody.match(
|
||||
new RegExp(
|
||||
`^\\*\\*Severity:\\*\\* ${rowSeverity}\\s*$`,
|
||||
"m"
|
||||
)
|
||||
) ||
|
||||
(
|
||||
activeFinding &&
|
||||
(
|
||||
activeFinding.title !== rowTitle ||
|
||||
activeFinding.severity !== rowSeverity
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
"Investigation report title or severity does not match the pending row"
|
||||
);
|
||||
}
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: issueNumber,
|
||||
|
||||
@@ -2148,6 +2148,35 @@ class TokenFailoverTests(unittest.TestCase):
|
||||
["get-run", "get-issue"],
|
||||
)
|
||||
|
||||
misleading = run_investigation_publisher(
|
||||
self,
|
||||
report_body=(
|
||||
"## 🔍 Investigation: Misleading title\n\n"
|
||||
"**Finding ID:** `pipeline:evaluation:evaluate:test:failure`\n"
|
||||
"**Severity:** critical\n"
|
||||
"**Correlation:** hc-123-1\n"
|
||||
"**Executive Summary:** Tests failed.\n\n"
|
||||
"### Root Cause\nA deterministic failure was confirmed.\n\n"
|
||||
"**Confidence:** High — the assertion identifies the cause.\n\n"
|
||||
"### Blast Radius\nThe evaluation workflow is affected.\n\n"
|
||||
"### Suggested Fix\n1. Correct the test setup.\n\n"
|
||||
"### Remediation Status\nReport-only. A maintainer should fix it.\n\n"
|
||||
"**Validation:** Run the targeted test.\n"
|
||||
"**Owner:** Evaluation maintainers\n\n"
|
||||
"### Evidence\nThe workflow output confirms the failure.\n\n"
|
||||
"### Related\nNone found."
|
||||
),
|
||||
)
|
||||
self.assertFalse(misleading["ok"])
|
||||
self.assertIn(
|
||||
"title or severity does not match the pending row",
|
||||
misleading["error"],
|
||||
)
|
||||
self.assertEqual(
|
||||
[call["type"] for call in misleading["calls"]],
|
||||
["get-run", "get-issue"],
|
||||
)
|
||||
|
||||
def test_devops_health_investigator_has_no_mutating_tools(self) -> None:
|
||||
workflows = REPO_ROOT / ".github" / "workflows"
|
||||
investigate_source = workflows / "devops-health-investigate.md"
|
||||
|
||||
Reference in New Issue
Block a user