Skip to content

Commit 57583df

Browse files
chore: sync actions from gh-aw@v0.68.4 (#77)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent abea67e commit 57583df

33 files changed

Lines changed: 3109 additions & 104 deletions

setup/js/add_reviewer.cjs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,18 @@
33

44
/**
55
* @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction
6+
* @typedef {import('./types/handler-factory').HandlerConfig} HandlerConfig
7+
* @typedef {import('./types/handler-factory').ResolvedTemporaryIds} ResolvedTemporaryIds
8+
* @typedef {import('./types/handler-factory').HandlerResult} HandlerResult
69
*/
710

11+
/**
12+
* @typedef {{ reviewers?: Array<string|null|undefined|false>, pull_request_number?: number|string }} AddReviewerMessage
13+
*/
14+
15+
/** @type {string} Safe output type handled by this module */
16+
const HANDLER_TYPE = "add_reviewer";
17+
818
const { processItems } = require("./safe_output_processor.cjs");
919
const { getErrorMessage } = require("./error_helpers.cjs");
1020
const { getPullRequestNumber } = require("./pr_helpers.cjs");
@@ -32,9 +42,9 @@ async function main(config = {}) {
3242
let processedCount = 0;
3343

3444
/**
35-
* @param {Object} message - The add_reviewer message to process
36-
* @param {Object} resolvedTemporaryIds - Map of temporary IDs to {repo, number}
37-
* @returns {Promise<Object>} Result with success/error status
45+
* @param {AddReviewerMessage} message - The add_reviewer message to process
46+
* @param {ResolvedTemporaryIds} resolvedTemporaryIds - Map of temporary IDs to {repo, number}
47+
* @returns {Promise<HandlerResult>} Result with success/error status
3848
*/
3949
return async function handleAddReviewer(message, resolvedTemporaryIds) {
4050
if (processedCount >= maxCount) {

setup/js/close_expired_discussions.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { executeExpiredEntityCleanup } = require("./expired_entity_main_flow.cjs"
55
const { generateExpiredEntityFooter } = require("./generate_footer.cjs");
66
const { sanitizeContent } = require("./sanitize_content.cjs");
77
const { getWorkflowMetadata } = require("./workflow_metadata_helpers.cjs");
8+
const { resolveExecutionOwnerRepo } = require("./repo_helpers.cjs");
89

910
/**
1011
* Add comment to a GitHub Discussion using GraphQL
@@ -90,8 +91,8 @@ async function hasExpirationComment(github, discussionId) {
9091
}
9192

9293
async function main() {
93-
const owner = context.repo.owner;
94-
const repo = context.repo.repo;
94+
const { owner, repo } = resolveExecutionOwnerRepo();
95+
core.info(`Operating on repository: ${owner}/${repo}`);
9596

9697
// Get workflow metadata for footer
9798
const { workflowName, workflowId, runUrl } = getWorkflowMetadata(owner, repo);

setup/js/close_expired_issues.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { executeExpiredEntityCleanup } = require("./expired_entity_main_flow.cjs"
55
const { generateExpiredEntityFooter } = require("./generate_footer.cjs");
66
const { sanitizeContent } = require("./sanitize_content.cjs");
77
const { getWorkflowMetadata } = require("./workflow_metadata_helpers.cjs");
8+
const { resolveExecutionOwnerRepo } = require("./repo_helpers.cjs");
89

910
/**
1011
* Add comment to a GitHub Issue using REST API
@@ -47,8 +48,8 @@ async function closeIssue(github, owner, repo, issueNumber) {
4748
}
4849

4950
async function main() {
50-
const owner = context.repo.owner;
51-
const repo = context.repo.repo;
51+
const { owner, repo } = resolveExecutionOwnerRepo();
52+
core.info(`Operating on repository: ${owner}/${repo}`);
5253

5354
// Get workflow metadata for footer
5455
const { workflowName, workflowId, runUrl } = getWorkflowMetadata(owner, repo);

setup/js/close_expired_pull_requests.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { executeExpiredEntityCleanup } = require("./expired_entity_main_flow.cjs"
55
const { generateExpiredEntityFooter } = require("./generate_footer.cjs");
66
const { sanitizeContent } = require("./sanitize_content.cjs");
77
const { getWorkflowMetadata } = require("./workflow_metadata_helpers.cjs");
8+
const { resolveExecutionOwnerRepo } = require("./repo_helpers.cjs");
89

910
/**
1011
* Add comment to a GitHub Pull Request using REST API
@@ -46,8 +47,8 @@ async function closePullRequest(github, owner, repo, prNumber) {
4647
}
4748

4849
async function main() {
49-
const owner = context.repo.owner;
50-
const repo = context.repo.repo;
50+
const { owner, repo } = resolveExecutionOwnerRepo();
51+
core.info(`Operating on repository: ${owner}/${repo}`);
5152

5253
// Get workflow metadata for footer
5354
const { workflowName, workflowId, runUrl } = getWorkflowMetadata(owner, repo);
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// @ts-check
2+
"use strict";
3+
4+
// Ensures global.core is available when running outside github-script context
5+
require("./shim.cjs");
6+
7+
/**
8+
* convert_gateway_config_claude.cjs
9+
*
10+
* Converts the MCP gateway's standard HTTP-based configuration to the JSON
11+
* format expected by Claude. Reads the gateway output JSON, filters out
12+
* CLI-mounted servers, sets type:"http", rewrites URLs to use the correct
13+
* domain, and writes the result to ${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json.
14+
*
15+
* Required environment variables:
16+
* - MCP_GATEWAY_OUTPUT: Path to gateway output configuration file
17+
* - MCP_GATEWAY_DOMAIN: Domain for MCP server URLs (e.g., host.docker.internal)
18+
* - MCP_GATEWAY_PORT: Port for MCP gateway (e.g., 80)
19+
* - RUNNER_TEMP: GitHub Actions runner temp directory
20+
*
21+
* Optional:
22+
* - GH_AW_MCP_CLI_SERVERS: JSON array of server names to exclude from agent config
23+
*/
24+
25+
const fs = require("fs");
26+
const path = require("path");
27+
28+
const OUTPUT_PATH = path.join(process.env.RUNNER_TEMP || "/tmp", "gh-aw/mcp-config/mcp-servers.json");
29+
30+
/**
31+
* Rewrite a gateway URL to use the configured domain and port.
32+
* Replaces http://<anything>/mcp/ with http://<domain>:<port>/mcp/.
33+
*
34+
* @param {string} url - Original URL from gateway output
35+
* @param {string} urlPrefix - Target URL prefix (e.g., http://host.docker.internal:80)
36+
* @returns {string} Rewritten URL
37+
*/
38+
function rewriteUrl(url, urlPrefix) {
39+
return url.replace(/^http:\/\/[^/]+\/mcp\//, `${urlPrefix}/mcp/`);
40+
}
41+
42+
function main() {
43+
const gatewayOutput = process.env.MCP_GATEWAY_OUTPUT;
44+
const domain = process.env.MCP_GATEWAY_DOMAIN;
45+
const port = process.env.MCP_GATEWAY_PORT;
46+
47+
if (!gatewayOutput) {
48+
core.error("ERROR: MCP_GATEWAY_OUTPUT environment variable is required");
49+
process.exit(1);
50+
}
51+
if (!fs.existsSync(gatewayOutput)) {
52+
core.error(`ERROR: Gateway output file not found: ${gatewayOutput}`);
53+
process.exit(1);
54+
}
55+
if (!domain) {
56+
core.error("ERROR: MCP_GATEWAY_DOMAIN environment variable is required");
57+
process.exit(1);
58+
}
59+
if (!port) {
60+
core.error("ERROR: MCP_GATEWAY_PORT environment variable is required");
61+
process.exit(1);
62+
}
63+
64+
core.info("Converting gateway configuration to Claude format...");
65+
core.info(`Input: ${gatewayOutput}`);
66+
core.info(`Target domain: ${domain}:${port}`);
67+
68+
const urlPrefix = `http://${domain}:${port}`;
69+
70+
/** @type {Set<string>} */
71+
const cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"));
72+
if (cliServers.size > 0) {
73+
core.info(`CLI-mounted servers to filter: ${[...cliServers].join(", ")}`);
74+
}
75+
76+
/** @type {Record<string, unknown>} */
77+
const config = JSON.parse(fs.readFileSync(gatewayOutput, "utf8"));
78+
const rawServers = config.mcpServers;
79+
const servers =
80+
/** @type {Record<string, Record<string, unknown>>} */
81+
rawServers && typeof rawServers === "object" && !Array.isArray(rawServers) ? rawServers : {};
82+
83+
/** @type {Record<string, Record<string, unknown>>} */
84+
const result = {};
85+
for (const [name, value] of Object.entries(servers)) {
86+
if (cliServers.has(name)) continue;
87+
const entry = { ...value };
88+
// Claude uses "type": "http" for HTTP-based MCP servers
89+
entry.type = "http";
90+
// Fix the URL to use the correct domain
91+
if (typeof entry.url === "string") {
92+
entry.url = rewriteUrl(entry.url, urlPrefix);
93+
}
94+
result[name] = entry;
95+
}
96+
97+
const output = JSON.stringify({ mcpServers: result }, null, 2);
98+
99+
const serverCount = Object.keys(result).length;
100+
const totalCount = Object.keys(servers).length;
101+
const filteredCount = totalCount - serverCount;
102+
core.info(`Servers: ${serverCount} included, ${filteredCount} filtered (CLI-mounted)`);
103+
104+
// Ensure output directory exists
105+
fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
106+
107+
// Write with owner-only permissions (0o600) to protect the gateway bearer token.
108+
// An attacker who reads mcp-servers.json could bypass --allowed-tools by issuing
109+
// raw JSON-RPC calls directly to the gateway.
110+
fs.writeFileSync(OUTPUT_PATH, output, { mode: 0o600 });
111+
112+
core.info(`Claude configuration written to ${OUTPUT_PATH}`);
113+
core.info("");
114+
core.info("Converted configuration:");
115+
core.info(output);
116+
}
117+
118+
main();
119+
120+
module.exports = { rewriteUrl };
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// @ts-check
2+
"use strict";
3+
4+
// Ensures global.core is available when running outside github-script context
5+
require("./shim.cjs");
6+
7+
/**
8+
* convert_gateway_config_codex.cjs
9+
*
10+
* Converts the MCP gateway's standard HTTP-based configuration to the TOML
11+
* format expected by Codex. Reads the gateway output JSON, filters out
12+
* CLI-mounted servers, resolves host.docker.internal to 172.30.0.1 for Rust
13+
* DNS compatibility, and writes the result to ${RUNNER_TEMP}/gh-aw/mcp-config/config.toml.
14+
*
15+
* Required environment variables:
16+
* - MCP_GATEWAY_OUTPUT: Path to gateway output configuration file
17+
* - MCP_GATEWAY_DOMAIN: Domain for MCP server URLs (e.g., host.docker.internal)
18+
* - MCP_GATEWAY_PORT: Port for MCP gateway (e.g., 80)
19+
* - RUNNER_TEMP: GitHub Actions runner temp directory
20+
*
21+
* Optional:
22+
* - GH_AW_MCP_CLI_SERVERS: JSON array of server names to exclude from agent config
23+
*/
24+
25+
const fs = require("fs");
26+
const path = require("path");
27+
28+
const OUTPUT_PATH = path.join(process.env.RUNNER_TEMP || "/tmp", "gh-aw/mcp-config/config.toml");
29+
30+
function main() {
31+
const gatewayOutput = process.env.MCP_GATEWAY_OUTPUT;
32+
const domain = process.env.MCP_GATEWAY_DOMAIN;
33+
const port = process.env.MCP_GATEWAY_PORT;
34+
35+
if (!gatewayOutput) {
36+
core.error("ERROR: MCP_GATEWAY_OUTPUT environment variable is required");
37+
process.exit(1);
38+
}
39+
if (!fs.existsSync(gatewayOutput)) {
40+
core.error(`ERROR: Gateway output file not found: ${gatewayOutput}`);
41+
process.exit(1);
42+
}
43+
if (!domain) {
44+
core.error("ERROR: MCP_GATEWAY_DOMAIN environment variable is required");
45+
process.exit(1);
46+
}
47+
if (!port) {
48+
core.error("ERROR: MCP_GATEWAY_PORT environment variable is required");
49+
process.exit(1);
50+
}
51+
52+
core.info("Converting gateway configuration to Codex TOML format...");
53+
core.info(`Input: ${gatewayOutput}`);
54+
core.info(`Target domain: ${domain}:${port}`);
55+
56+
// For host.docker.internal, resolve to the gateway IP to avoid DNS resolution
57+
// issues in Rust
58+
let resolvedDomain = domain;
59+
if (domain === "host.docker.internal") {
60+
// AWF network gateway IP is always 172.30.0.1
61+
resolvedDomain = "172.30.0.1";
62+
core.info(`Resolving host.docker.internal to gateway IP: ${resolvedDomain}`);
63+
}
64+
65+
const urlPrefix = `http://${resolvedDomain}:${port}`;
66+
67+
/** @type {Set<string>} */
68+
const cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"));
69+
if (cliServers.size > 0) {
70+
core.info(`CLI-mounted servers to filter: ${[...cliServers].join(", ")}`);
71+
}
72+
73+
/** @type {Record<string, unknown>} */
74+
const config = JSON.parse(fs.readFileSync(gatewayOutput, "utf8"));
75+
const rawServers = config.mcpServers;
76+
const servers =
77+
/** @type {Record<string, Record<string, unknown>>} */
78+
rawServers && typeof rawServers === "object" && !Array.isArray(rawServers) ? rawServers : {};
79+
80+
// Build the TOML output
81+
let toml = '[history]\npersistence = "none"\n\n';
82+
83+
for (const [name, value] of Object.entries(servers)) {
84+
if (cliServers.has(name)) continue;
85+
const url = `${urlPrefix}/mcp/${name}`;
86+
const headers = /** @type {Record<string, string>} */ value.headers || {};
87+
const authKey = headers.Authorization || "";
88+
toml += `[mcp_servers.${name}]\n`;
89+
toml += `url = "${url}"\n`;
90+
toml += `http_headers = { Authorization = "${authKey}" }\n`;
91+
toml += "\n";
92+
}
93+
94+
const includedCount = Object.keys(servers).length - [...Object.keys(servers)].filter(k => cliServers.has(k)).length;
95+
const filteredCount = Object.keys(servers).length - includedCount;
96+
core.info(`Servers: ${includedCount} included, ${filteredCount} filtered (CLI-mounted)`);
97+
98+
// Ensure output directory exists
99+
fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
100+
101+
// Write with owner-only permissions (0o600) to protect the gateway bearer token.
102+
// An attacker who reads config.toml could issue raw JSON-RPC calls directly
103+
// to the gateway.
104+
fs.writeFileSync(OUTPUT_PATH, toml, { mode: 0o600 });
105+
106+
core.info(`Codex configuration written to ${OUTPUT_PATH}`);
107+
core.info("");
108+
core.info("Converted configuration:");
109+
core.info(toml);
110+
}
111+
112+
main();
113+
114+
module.exports = {};

0 commit comments

Comments
 (0)