Skip to content

Commit 8c8a264

Browse files
chore: sync actions from gh-aw@v0.74.6 (#108)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 3360552 commit 8c8a264

8 files changed

Lines changed: 222 additions & 13 deletions

File tree

.github/aw/compat.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "./compat.schema.json",
3+
"agent-compat-v1": {
4+
"cache-ttl-days": 14,
5+
"copilot": [
6+
{
7+
"min-gh-aw": "0.72.0",
8+
"max-gh-aw": "*",
9+
"min-agent": "1.0.21",
10+
"max-agent": "1.0.48",
11+
"open": true
12+
},
13+
{
14+
"min-gh-aw": "0.0.1",
15+
"max-gh-aw": "0.71.99",
16+
"min-agent": "0.0.0",
17+
"max-agent": "1.0.21"
18+
}
19+
]
20+
}
21+
}

.github/aw/compat.schema.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://github.com/github/gh-aw/.github/aw/compat.schema.json",
4+
"title": "gh-aw agent compatibility matrix",
5+
"description": "Pins agentic CLI versions to gh-aw release ranges. Consumed by the setup action to install a known-good agent version per gh-aw release. See ADR for the design rationale.",
6+
"type": "object",
7+
"required": ["agent-compat-v1"],
8+
"additionalProperties": false,
9+
"properties": {
10+
"$schema": {
11+
"type": "string"
12+
},
13+
"agent-compat-v1": {
14+
"type": "object",
15+
"additionalProperties": false,
16+
"properties": {
17+
"cache-ttl-days": {
18+
"type": "integer",
19+
"minimum": 1,
20+
"description": "How long the setup action may cache the resolved agent install before re-fetching this matrix."
21+
},
22+
"copilot": {
23+
"$ref": "#/definitions/agentRows"
24+
},
25+
"claude": {
26+
"$ref": "#/definitions/agentRows"
27+
}
28+
}
29+
}
30+
},
31+
"definitions": {
32+
"semver": {
33+
"type": "string",
34+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?$"
35+
},
36+
"ghAwBound": {
37+
"oneOf": [
38+
{ "$ref": "#/definitions/semver" },
39+
{ "type": "string", "enum": ["*"] }
40+
]
41+
},
42+
"agentRows": {
43+
"type": "array",
44+
"minItems": 1,
45+
"items": { "$ref": "#/definitions/row" }
46+
},
47+
"row": {
48+
"type": "object",
49+
"required": ["min-gh-aw", "max-gh-aw", "min-agent", "max-agent"],
50+
"additionalProperties": false,
51+
"properties": {
52+
"min-gh-aw": { "$ref": "#/definitions/semver" },
53+
"max-gh-aw": { "$ref": "#/definitions/ghAwBound" },
54+
"min-agent": { "$ref": "#/definitions/semver" },
55+
"max-agent": { "$ref": "#/definitions/semver" },
56+
"open": {
57+
"type": "boolean",
58+
"default": true,
59+
"description": "When true (the default), the setup action's weekly release process may continue bumping max-agent for this row. Set to false to freeze during rollback or quarantine. Permitted only on the catch-all row (max-gh-aw == \"*\"); bounded rows are closed-by-construction."
60+
}
61+
},
62+
"if": {
63+
"properties": { "max-gh-aw": { "const": "*" } }
64+
},
65+
"then": {},
66+
"else": {
67+
"not": { "required": ["open"] }
68+
}
69+
}
70+
}
71+
}

setup/js/add_comment.cjs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -682,18 +682,59 @@ async function main(config = {}) {
682682
core.warning("Ignoring empty discussion reply_to_id after normalization");
683683
}
684684

685+
// add_comment uses snake_case fields. camelCase and kebab-case aliases are
686+
// accepted for compatibility with forwarded/legacy payload variants.
687+
const explicitCommentIdRaw = message.comment_id ?? message.commentId ?? message["comment-id"];
688+
const rawTarget = message.target;
689+
const allowedTargets = ["status"];
690+
if (rawTarget !== undefined && !allowedTargets.includes(rawTarget)) {
691+
return {
692+
success: false,
693+
error: `target must be one of: [${allowedTargets.join(", ")}]`,
694+
};
695+
}
696+
const isStatusCommentTarget = rawTarget === "status";
697+
const statusCommentIdRaw = process.env.GH_AW_COMMENT_ID || "";
698+
let commentIdToReuse = null;
699+
if (explicitCommentIdRaw !== undefined && explicitCommentIdRaw !== null && String(explicitCommentIdRaw).trim() !== "") {
700+
commentIdToReuse = Number(explicitCommentIdRaw);
701+
if (!Number.isInteger(commentIdToReuse) || commentIdToReuse <= 0) {
702+
return {
703+
success: false,
704+
error: "comment_id must be a positive integer",
705+
};
706+
}
707+
} else if (isStatusCommentTarget) {
708+
const parsedStatusCommentId = Number(statusCommentIdRaw);
709+
if (Number.isInteger(parsedStatusCommentId) && parsedStatusCommentId > 0) {
710+
commentIdToReuse = parsedStatusCommentId;
711+
} else {
712+
core.info("target=status was requested but no reusable status comment id was available; creating a new comment");
713+
}
714+
}
715+
685716
try {
686717
// Hide older comments if enabled AND append-only-comments is not enabled
687718
// When append-only-comments is true, we want to keep all comments visible
688-
if (hideOlderCommentsEnabled && !appendOnlyComments && workflowId) {
689-
await hideOlderComments(githubClient, repoParts.owner, repoParts.repo, itemNumber, workflowId, isDiscussion);
690-
} else if (hideOlderCommentsEnabled && appendOnlyComments) {
691-
core.info("Skipping hide-older-comments because append-only-comments is enabled");
719+
if (hideOlderCommentsEnabled) {
720+
if (commentIdToReuse !== null) {
721+
core.info("Skipping hide-older-comments because an existing comment is being updated");
722+
} else if (appendOnlyComments) {
723+
core.info("Skipping hide-older-comments because append-only-comments is enabled");
724+
} else if (workflowId) {
725+
await hideOlderComments(githubClient, repoParts.owner, repoParts.repo, itemNumber, workflowId, isDiscussion);
726+
}
692727
}
693728

694729
/** @type {{ id: string | number, html_url: string }} */
695730
let comment;
696731
if (isDiscussion) {
732+
if (commentIdToReuse !== null) {
733+
return {
734+
success: false,
735+
error: "comment_id and target=status are only supported for issue and pull request comments",
736+
};
737+
}
697738
// When triggered by a discussion_comment event (without explicit item_number),
698739
// reply as a threaded comment to the triggering comment instead of posting top-level.
699740
// GitHub Discussions only supports two nesting levels, so if the triggering comment is
@@ -729,6 +770,15 @@ async function main(config = {}) {
729770
body: processedBody,
730771
});
731772
comment = data;
773+
} else if (commentIdToReuse !== null) {
774+
core.info(`Updating existing comment ID: ${commentIdToReuse}`);
775+
const { data } = await githubClient.rest.issues.updateComment({
776+
owner: repoParts.owner,
777+
repo: repoParts.repo,
778+
comment_id: commentIdToReuse,
779+
body: processedBody,
780+
});
781+
comment = data;
732782
} else {
733783
if (shouldReplyToTriggeringPRReviewComment) {
734784
core.warning("Triggering PR review comment ID is missing or invalid; falling back to top-level PR comment");

setup/js/model_multipliers.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
"gemini-3.1-flash-lite-preview": 0.1,
132132
"gemini-3.1-flash-image-preview": 0.33,
133133
"gemini-3.1-flash-tts-preview": 0.1,
134+
"gemini-3.5-flash": 14.0,
134135
"gemini-2.5-computer-use-preview": 0.2,
135136
"gemini-2.5-computer-use-preview-10-2025": 0.2,
136137
"gemini-robotics-er-1.5-preview": 0.2,

setup/js/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
"@actions/github-script": "github:actions/github-script#v9.0.0",
88
"@actions/glob": "^0.7.0",
99
"@actions/io": "^3.0.2",
10-
"@types/node": "^25.7.0",
10+
"@types/node": "^25.9.1",
1111
"@vitest/coverage-v8": "^4.1.4",
1212
"@vitest/ui": "^4.1.6",
1313
"minimatch": ">=3.1.3",
1414
"prettier": "^3.8.3",
1515
"typescript": "^6.0.3",
16-
"vite": "^8.0.12",
16+
"vite": "^8.0.13",
1717
"vitest": "^4.1.6"
1818
},
1919
"overrides": {

setup/js/parse_mcp_gateway_log.cjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
const fs = require("fs");
55
const { getErrorMessage } = require("./error_helpers.cjs");
66
const { displayDirectories } = require("./display_file_helpers.cjs");
7-
const { ERR_PARSE } = require("./error_codes.cjs");
7+
const { ERR_PARSE, ERR_SYSTEM } = require("./error_codes.cjs");
88
const { computeEffectiveTokens, getTokenClassWeights, formatET } = require("./effective_tokens.cjs");
99

1010
/**
@@ -711,6 +711,10 @@ async function main() {
711711
} else if (fs.existsSync(rpcMessagesPath)) {
712712
rpcMessagesContent = fs.readFileSync(rpcMessagesPath, "utf8");
713713
core.info(`Found rpc-messages.jsonl (${rpcMessagesContent.length} bytes)`);
714+
if (rpcMessagesContent.length === 0) {
715+
core.setFailed(`${ERR_SYSTEM}: rpc-messages.jsonl is present but zero bytes — MCP telemetry capture failed (server may not have started or crashed before any RPC)`);
716+
return;
717+
}
714718
difcFilteredEvents = parseGatewayJsonlForDifcFiltered(rpcMessagesContent);
715719
tokenSteeringEvents = parseGatewayJsonlForTokenSteering(rpcMessagesContent);
716720
effectiveTokensRateLimitError ||= hasEffectiveTokensRateLimitError([rpcMessagesContent]);

setup/js/pr_review_buffer.cjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,12 @@ function createReviewBuffer() {
235235
}
236236

237237
if (!reviewContext) {
238-
core.warning("No review context set - cannot submit review");
239-
return { success: false, error: "No review context available" };
238+
core.info("No review context set - skipping PR review submission");
239+
return {
240+
success: true,
241+
skipped: true,
242+
reason: "No review context available",
243+
};
240244
}
241245

242246
const { repo, repoParts, pullRequestNumber, pullRequest } = reviewContext;

setup/js/route_slash_command.cjs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/// <reference types="@actions/github-script" />
33

44
const { REACTION_MAP } = require("./add_reaction.cjs");
5+
const { extractWorkflowId } = require("./generate_footer.cjs");
56
// Keep this aligned with the current default stable GitHub REST API version used by workflows.
67
// Update when GitHub advances the recommended version to avoid sunset/deprecation warnings.
78
const GITHUB_API_VERSION = "2022-11-28";
@@ -19,12 +20,25 @@ function resolveBodyText() {
1920
pull_request: context.payload?.pull_request?.body ?? "",
2021
issue_comment: context.payload?.comment?.body ?? "",
2122
pull_request_review_comment: context.payload?.comment?.body ?? "",
23+
pull_request_review: context.payload?.review?.body ?? "",
2224
discussion: context.payload?.discussion?.body ?? "",
2325
discussion_comment: context.payload?.comment?.body ?? "",
2426
};
2527
return bodyByEvent[context.eventName] ?? "";
2628
}
2729

30+
function isPRClosedAtStart() {
31+
const pullRequestState = context.payload?.pull_request?.state;
32+
if (pullRequestState === "closed") {
33+
return true;
34+
}
35+
const issueState = context.payload?.issue?.state;
36+
if (context.payload?.issue?.pull_request && issueState === "closed") {
37+
return true;
38+
}
39+
return false;
40+
}
41+
2842
function resolveDispatchRef() {
2943
if (process.env.GITHUB_HEAD_REF) {
3044
return `refs/heads/${process.env.GITHUB_HEAD_REF}`;
@@ -194,10 +208,8 @@ async function dispatchWorkflow(workflowId, ref, inputs) {
194208
workflow_id: workflowId,
195209
ref,
196210
inputs,
197-
request: {
198-
headers: {
199-
"X-GitHub-Api-Version": GITHUB_API_VERSION,
200-
},
211+
headers: {
212+
"X-GitHub-Api-Version": GITHUB_API_VERSION,
201213
},
202214
});
203215
} catch (error) {
@@ -211,12 +223,18 @@ async function main() {
211223

212224
const slashRouteMap = JSON.parse(process.env.GH_AW_SLASH_ROUTING || "{}");
213225
const labelRouteMap = JSON.parse(process.env.GH_AW_LABEL_ROUTING || "{}");
226+
const reviewerRoutes = JSON.parse(process.env.GH_AW_REVIEWER_ROUTING || "[]");
214227
core.info(`Configured centralized slash commands: ${Object.keys(slashRouteMap).length}.`);
215228
core.info(`Configured decentralized label commands: ${Object.keys(labelRouteMap).length}.`);
229+
core.info(`Configured pull-request reviewer workflows: ${Array.isArray(reviewerRoutes) ? reviewerRoutes.length : 0}.`);
216230

217231
const identifier = eventIdentifier();
218232
const { buildAwContext } = require("./aw_context.cjs");
219233
const ref = resolveDispatchRef();
234+
if (isPRClosedAtStart()) {
235+
core.info("Pull request is closed at workflow start; skipping centralized routing.");
236+
return;
237+
}
220238

221239
if (context.payload?.action === "labeled") {
222240
const labelName = context.payload?.label?.name ?? "";
@@ -254,6 +272,46 @@ async function main() {
254272
return;
255273
}
256274

275+
if ((context.eventName === "pull_request" || context.eventName === "pull_request_review") && Array.isArray(reviewerRoutes) && reviewerRoutes.length > 0) {
276+
const matches = reviewerRoutes.filter(route => Array.isArray(route.events) && route.events.includes(context.eventName));
277+
if (matches.length > 0) {
278+
let selected = matches;
279+
if (context.eventName === "pull_request") {
280+
if (!["ready_for_review", "review_requested"].includes(context.payload?.action ?? "")) {
281+
selected = [];
282+
}
283+
} else if (context.eventName === "pull_request_review") {
284+
const action = context.payload?.action ?? "";
285+
if (action !== "submitted") {
286+
selected = [];
287+
} else {
288+
const workflowId = extractWorkflowId(context.payload?.review?.body ?? "");
289+
if (workflowId) {
290+
selected = selected.filter(route => route.workflow === workflowId);
291+
} else {
292+
selected = [];
293+
core.info("No workflow marker found in pull request review body; skipping reviewer dispatch.");
294+
}
295+
}
296+
}
297+
if (selected.length > 0) {
298+
core.info(`Matched reviewer routes on '${context.eventName}': ${selected.map(route => route.workflow).join(", ")}.`);
299+
for (const route of selected) {
300+
const awContext = {
301+
...buildAwContext(),
302+
command_name: "",
303+
reviewer_lifecycle_event: context.eventName,
304+
};
305+
core.info(`Dispatching reviewer workflow '${route.workflow}.lock.yml'.`);
306+
await dispatchWorkflow(`${route.workflow}.lock.yml`, ref, {
307+
aw_context: JSON.stringify(awContext),
308+
});
309+
}
310+
core.info("Completed reviewer lifecycle routing.");
311+
}
312+
}
313+
}
314+
257315
const text = resolveBodyText();
258316
core.info(`Resolved payload text length: ${String(text).length}.`);
259317
const firstWord = String(text).trim().split(/\s+/)[0] ?? "";

0 commit comments

Comments
 (0)