Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion actions/setup/js/add_reaction_and_edit_comment.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@ async function resolveEventEndpoints(eventName, owner, repo, payload) {
async function main() {
const reaction = process.env.GH_AW_REACTION || "eyes";
const commandsJSON = process.env.GH_AW_COMMANDS;
const command = commandsJSON ? (JSON.parse(commandsJSON)[0] ?? null) : null; // Only present for command workflows
let command = null; // Only present for command workflows
if (commandsJSON) {
try {
command = JSON.parse(commandsJSON)[0] ?? null;
} catch (err) {
throw new Error("Failed to parse GH_AW_COMMANDS: " + getErrorMessage(err), { cause: err });
}
}
const invocationContext = resolveInvocationContext(context);
const runUrl = buildWorkflowRunUrl(context, invocationContext.workflowRepo);

Expand Down
7 changes: 6 additions & 1 deletion actions/setup/js/apply_safe_outputs_replay.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,12 @@ async function downloadAgentArtifact(runId, destDir, repoSlug) {
*/
function buildHandlerConfigFromOutput(agentOutputFile) {
const content = fs.readFileSync(agentOutputFile, "utf8");
const validatedOutput = JSON.parse(content);
let validatedOutput;
try {
validatedOutput = JSON.parse(content);
} catch (err) {
throw new Error("Failed to parse agent output file " + agentOutputFile + ": " + getErrorMessage(err), { cause: err });
}

if (!validatedOutput.items || !Array.isArray(validatedOutput.items)) {
core.info("No items found in agent output; handler config will be empty");
Expand Down
10 changes: 9 additions & 1 deletion actions/setup/js/artifact_client.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const { Readable } = require("stream");
const { pipeline } = require("stream/promises");
const { spawnSync } = require("child_process");

const { getErrorMessage } = require("./error_helpers.cjs");

const DEFAULT_RETRY_ATTEMPTS = 5;
const RETRY_DELAY_MS = 5000;
const RESULTS_SCOPE_PREFIX = "Actions.Results:";
Expand All @@ -33,7 +35,13 @@ function decodeJWTPayload(token) {
}
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const padded = payload + "=".repeat((4 - (payload.length % 4 || 4)) % 4);
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
let parsed;
try {
parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
} catch (err) {
throw new Error("Failed to parse JWT payload: " + getErrorMessage(err), { cause: err });
}
return parsed;
}

function getBackendIdsFromRuntimeToken() {
Expand Down
7 changes: 6 additions & 1 deletion actions/setup/js/build_checkout_manifest.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ const { execFileSync } = require("child_process");
const { getErrorMessage } = require("./error_helpers.cjs");

function parseManifestEntries(entriesJSON = process.env.GH_AW_CHECKOUT_MANIFEST_ENTRIES || "[]") {
const parsed = JSON.parse(entriesJSON);
let parsed;
try {
parsed = JSON.parse(entriesJSON);
} catch (err) {
throw new Error("Failed to parse GH_AW_CHECKOUT_MANIFEST_ENTRIES: " + getErrorMessage(err), { cause: err });
}
if (!Array.isArray(parsed)) {
throw new Error("GH_AW_CHECKOUT_MANIFEST_ENTRIES must be a JSON array");
}
Expand Down
16 changes: 14 additions & 2 deletions actions/setup/js/convert_gateway_config_shared.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
const fs = require("fs");
const path = require("path");

const { getErrorMessage } = require("./error_helpers.cjs");

/**
* Rewrite a gateway URL to use the configured domain and port.
* Replaces http://<anything>/mcp/ with http://<domain>:<port>/mcp/.
Expand Down Expand Up @@ -79,10 +81,20 @@ function loadGatewayContext(options = {}) {
}

/** @type {Set<string>} */
const cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"));
let cliServers;
try {
cliServers = new Set(JSON.parse(process.env.GH_AW_MCP_CLI_SERVERS || "[]"));
} catch (err) {
throw new Error("Failed to parse GH_AW_MCP_CLI_SERVERS: " + getErrorMessage(err), { cause: err });
}

/** @type {Record<string, unknown>} */
const config = JSON.parse(fs.readFileSync(gatewayOutput, "utf8"));
let config;
try {
config = JSON.parse(fs.readFileSync(gatewayOutput, "utf8"));
} catch (err) {
throw new Error("Failed to parse gateway output file " + gatewayOutput + ": " + getErrorMessage(err), { cause: err });
}
const rawServers = config.mcpServers;
/** @type {Record<string, Record<string, unknown>>} */
let servers = {};
Expand Down
28 changes: 24 additions & 4 deletions actions/setup/js/generate_safe_outputs_tools.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
const fs = require("fs");
const path = require("path");
const { ERR_CONFIG } = require("./error_codes.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");
const { parseRuntimeFeatures, hasRuntimeFeature } = require("./runtime_features.cjs");

const ADD_COMMENT_DEFAULT_DISCUSSIONS_NOTE =
Expand Down Expand Up @@ -95,7 +96,12 @@ async function main() {
throw new Error(msg);
}
/** @type {Array<{name: string, description: string, inputSchema?: {properties?: Record<string, unknown>}}>} */
const allTools = JSON.parse(fs.readFileSync(toolsSourcePath, "utf8"));
let allTools;
try {
allTools = JSON.parse(fs.readFileSync(toolsSourcePath, "utf8"));
} catch (err) {
throw new Error("Failed to parse tools source file " + toolsSourcePath + ": " + getErrorMessage(err), { cause: err });
}

// Load config to determine which tools are enabled
if (!fs.existsSync(configPath)) {
Expand All @@ -104,13 +110,22 @@ async function main() {
throw new Error(msg);
}
/** @type {Record<string, unknown>} */
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
let config;
try {
config = JSON.parse(fs.readFileSync(configPath, "utf8"));
} catch (err) {
throw new Error("Failed to parse config file " + configPath + ": " + getErrorMessage(err), { cause: err });
}

// Load tools meta (description suffixes, repo params, dynamic tools)
/** @type {{description_suffixes?: Record<string, string>, repo_params?: Record<string, {type: string, description: string}>, dynamic_tools?: Array<unknown>, required_field_removals?: Record<string, string[]>, required_field_additions?: Record<string, string[]>}} */
let toolsMeta = { description_suffixes: {}, repo_params: {}, dynamic_tools: [] };
if (fs.existsSync(toolsMetaPath)) {
toolsMeta = JSON.parse(fs.readFileSync(toolsMetaPath, "utf8"));
try {
toolsMeta = JSON.parse(fs.readFileSync(toolsMetaPath, "utf8"));
} catch (err) {
throw new Error("Failed to parse tools meta file " + toolsMetaPath + ": " + getErrorMessage(err), { cause: err });
}
}

// Build set of source tool names (predefined/static tools only)
Expand All @@ -127,7 +142,12 @@ async function main() {
.filter(tool => enabledToolNames.has(tool.name))
.map(tool => {
// Deep copy to avoid modifying the original
const enhancedTool = JSON.parse(JSON.stringify(tool));
let enhancedTool;
try {
enhancedTool = JSON.parse(JSON.stringify(tool));
} catch (err) {
throw new Error("Failed to deep-copy tool " + tool.name + ": " + getErrorMessage(err), { cause: err });
}

// Apply description suffix if available (e.g., " CONSTRAINTS: Maximum 5 issues.")
const descSuffix = toolsMeta.description_suffixes?.[tool.name];
Expand Down
8 changes: 7 additions & 1 deletion actions/setup/js/generate_workflow_overview.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/// <reference types="@actions/github-script" />

const { jsonObjectToMarkdown } = require("./json_object_to_markdown.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");

/**
* Generate workflow overview step that writes an agentic workflow run overview
Expand All @@ -16,7 +17,12 @@ async function generateWorkflowOverview(core) {
const awInfoPath = "/tmp/gh-aw/aw_info.json";

// Load aw_info.json
const awInfo = JSON.parse(fs.readFileSync(awInfoPath, "utf8"));
let awInfo;
try {
awInfo = JSON.parse(fs.readFileSync(awInfoPath, "utf8"));
} catch (err) {
throw new Error("Failed to parse aw_info.json at " + awInfoPath + ": " + getErrorMessage(err), { cause: err });
}

// Build the collapsible summary label with engine_id and version
const engineLabel = [awInfo.engine_id, awInfo.version].filter(Boolean).join(" ");
Expand Down
8 changes: 7 additions & 1 deletion actions/setup/js/mcp_scripts_config_loader.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

const fs = require("fs");
const { ERR_SYSTEM, ERR_VALIDATION } = require("./error_codes.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");

/**
* @typedef {Object} MCPScriptsToolConfig
Expand Down Expand Up @@ -40,7 +41,12 @@ function loadConfig(configPath) {
}

const configContent = fs.readFileSync(configPath, "utf-8");
const config = JSON.parse(configContent);
let config;
try {
config = JSON.parse(configContent);
} catch (err) {
throw new Error("Failed to parse config file " + configPath + ": " + getErrorMessage(err), { cause: err });
}

// Validate required fields
if (!config.tools || !Array.isArray(config.tools)) {
Expand Down
9 changes: 8 additions & 1 deletion actions/setup/js/patch_awf_chroot_config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const fs = require("fs");
const os = require("os");
const path = require("path");

const { getErrorMessage } = require("./error_helpers.cjs");

/**
* Patch the AWF config file with chroot settings for ARC/DinD runners.
*
Expand All @@ -23,7 +25,12 @@ function patchAWFChrootConfig(options = {}) {
const identityHome = options.identityHome || process.env.GH_AW_CHROOT_IDENTITY_HOME || "/tmp/gh-aw/home";
const configPath = path.join(runnerTemp, "gh-aw", "awf-config.json");
const artifactConfigPath = path.join(binariesSourcePath, "awf-config.json");
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
let config;
try {
config = JSON.parse(fs.readFileSync(configPath, "utf8"));
} catch (err) {
throw new Error("Failed to parse AWF config file " + configPath + ": " + getErrorMessage(err), { cause: err });
}
const userInfo = os.userInfo();

config.chroot = {
Expand Down
15 changes: 13 additions & 2 deletions actions/setup/js/route_slash_command.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { REACTION_MAP } = require("./add_reaction.cjs");
const { createOrReuseStatusComment } = require("./add_workflow_run_comment.cjs");
const nodePath = require("node:path");
const { matchesCommandName, parseSlashCommand } = require("./slash_command_matcher.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");
// Keep this aligned with the current default stable GitHub REST API version used by workflows.
// Update when GitHub advances the recommended version to avoid sunset/deprecation warnings.
const GITHUB_API_VERSION = "2022-11-28";
Expand Down Expand Up @@ -638,8 +639,18 @@ async function main() {
core.info("Starting centralized command routing.");
core.info(`Incoming event name: '${context.eventName}'.`);

const slashRouteMap = JSON.parse(process.env.GH_AW_SLASH_ROUTING || "{}");
const labelRouteMap = JSON.parse(process.env.GH_AW_LABEL_ROUTING || "{}");
let slashRouteMap;
try {
slashRouteMap = JSON.parse(process.env.GH_AW_SLASH_ROUTING || "{}");
} catch (err) {
throw new Error("Failed to parse GH_AW_SLASH_ROUTING: " + getErrorMessage(err), { cause: err });
}
let labelRouteMap;
try {
labelRouteMap = JSON.parse(process.env.GH_AW_LABEL_ROUTING || "{}");
} catch (err) {
throw new Error("Failed to parse GH_AW_LABEL_ROUTING: " + getErrorMessage(err), { cause: err });
}
core.info(`Configured centralized slash commands: ${Object.keys(slashRouteMap).length}.`);
core.info(`Configured decentralized label commands: ${Object.keys(labelRouteMap).length}.`);
const text = resolveBodyText();
Expand Down
8 changes: 7 additions & 1 deletion actions/setup/js/safe_outputs_handlers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ function resolveEffectiveContext(invocationContext, rawContext) {
* @returns {any}
*/
function readJSONFile(filePath) {
return JSON.parse(fs.readFileSync(filePath, "utf8"));
let parsed;
try {
parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
} catch (err) {
throw new Error("Failed to parse JSON file " + filePath + ": " + getErrorMessage(err), { cause: err });
}
return parsed;
}

const safeOutputsTools = readJSONFile(path.join(__dirname, "safe_outputs_tools.json"));
Expand Down
6 changes: 5 additions & 1 deletion actions/setup/js/safe_outputs_tools_loader.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ function registerPredefinedTools(server, tools, config, registerTool, normalizeT
const isCreatePullRequestTool = tool.name === "create_pull_request" && config.create_pull_request;
// Enrich create_pull_request tool description when target-repo is configured
if (safetyWarning || isCreatePullRequestTool) {
toolToRegister = JSON.parse(JSON.stringify(tool));
try {
toolToRegister = JSON.parse(JSON.stringify(tool));
} catch (err) {
throw new Error("Failed to deep-copy tool " + tool.name + ": " + getErrorMessage(err), { cause: err });
}
if (tool.handler) {
toolToRegister.handler = tool.handler;
}
Expand Down
8 changes: 7 additions & 1 deletion actions/setup/js/start_mcp_gateway.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const http = require("http");
const path = require("path");
const { withRetry } = require("./error_recovery.cjs");
const { lstatGuard } = require("./symlink_guard.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");

// ---------------------------------------------------------------------------
// Timing helpers
Expand Down Expand Up @@ -743,7 +744,12 @@ async function main() {
fs.chmodSync(outputPath, 0o600);

// Check for error payload
const gatewayOutput = JSON.parse(fs.readFileSync(outputPath, "utf8"));
let gatewayOutput;
try {
gatewayOutput = JSON.parse(fs.readFileSync(outputPath, "utf8"));
} catch (err) {
throw new Error("Failed to parse gateway output file " + outputPath + ": " + getErrorMessage(err), { cause: err });
}
if (gatewayOutput.error) {
core.error("ERROR: Gateway returned an error payload instead of configuration");
core.error("");
Expand Down
Loading