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
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ check-cjs-syntax:
.PHONY: test-js
test-js: build-js
cd actions/setup/js && npm run test:js -- --no-file-parallelism
cd eslint-factory && npm test

# Test impacted JavaScript unit tests only (excluding integration tests)
.PHONY: test-impacted-js
Expand Down Expand Up @@ -806,6 +807,7 @@ deps: check-node-version
go mod download
go mod tidy
cd actions/setup/js && npm ci
cd eslint-factory && npm ci

# Install development tools (including linter)
.PHONY: deps-dev
Expand Down
17 changes: 15 additions & 2 deletions actions/setup/js/add_comment.test.cjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
// @ts-check
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll } from "vitest";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { syncRuntimePromptTemplates } from "./test_prompt_templates.js";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
syncRuntimePromptTemplates(import.meta.url);
const { runtimePromptsDir } = syncRuntimePromptTemplates(import.meta.url);
const originalPromptsDir = process.env.GH_AW_PROMPTS_DIR;

beforeAll(() => {
process.env.GH_AW_PROMPTS_DIR = runtimePromptsDir;
});

afterAll(() => {
if (originalPromptsDir === undefined) {
delete process.env.GH_AW_PROMPTS_DIR;
} else {
process.env.GH_AW_PROMPTS_DIR = originalPromptsDir;
}
});

describe("add_comment", () => {
let mockCore;
Expand Down
19 changes: 17 additions & 2 deletions actions/setup/js/awf_reflect.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,23 @@ const tls = require("tls");
const { withRetry, sleep } = require("./error_recovery.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");

function parseReflectTimeoutMs(value) {
const rawValue = String(value || "").trim();
if (!/^\d+$/.test(rawValue)) {
return 60000;
}
const timeoutMs = Number(rawValue);
return Number.isSafeInteger(timeoutMs) ? timeoutMs : 60000;
}

// AWF API proxy management endpoint for discovering configured LLM providers and available models.
// The api-proxy sidecar exposes /reflect on its management port (port 10000) inside the AWF
// Docker network. From the agent container, the proxy is reachable via the "api-proxy" hostname.
const AWF_API_PROXY_REFLECT_URL = "http://api-proxy:10000/reflect";
// Persist outside the read-only gh-aw infrastructure mount.
const AWF_REFLECT_OUTPUT_PATH = path.join(process.env.RUNNER_TEMP || os.tmpdir(), "awf-reflect.json");
// Milliseconds to wait for the /reflect endpoint before giving up.
const AWF_REFLECT_TIMEOUT_MS = 60000;
const AWF_REFLECT_TIMEOUT_MS = parseReflectTimeoutMs(process.env.GH_AW_REFLECT_TIMEOUT_MS);
// Milliseconds to wait for each models_url fallback fetch (shorter than the main reflect timeout).
const AWF_MODELS_URL_TIMEOUT_MS = 3000;
// Milliseconds to wait for an api-proxy provider listener to accept a real TCP connection.
Expand Down Expand Up @@ -367,7 +376,7 @@ async function enrichReflectModels(reflectData, timeoutMs, logger) {
* outputPath: string,
* bytesWritten?: number,
* reflectData?: object,
* reason?: "unexpected_status"|"timeout"|"request_failed",
* reason?: "disabled"|"unexpected_status"|"timeout"|"request_failed",
* status?: number,
* error?: string,
* }>}
Expand All @@ -380,6 +389,11 @@ async function fetchAWFReflect(options) {
const logger = (options && options.logger) || DEFAULT_REFLECT_LOGGER;
const writeFile = (options && options.writeFileSync) || fs.writeFileSync;

if (process.env.GH_AW_SKIP_REFLECT === "true") {
logger("awf-reflect: disabled by GH_AW_SKIP_REFLECT");
return { ok: false, reflectUrl, outputPath, reason: "disabled" };
}

logger(`awf-reflect: fetching ${reflectUrl} (timeout=${timeoutMs}ms)`);

const ac = new AbortController();
Expand Down Expand Up @@ -1022,6 +1036,7 @@ if (typeof module !== "undefined" && module.exports) {
AWF_PROVIDER_LISTENER_READY_PROBE_TIMEOUT_MS,
DEFAULT_API_PROXY_HOST_BRIDGE,
GEMINI_MODEL_NAME_PREFIX,
parseReflectTimeoutMs,
enrichReflectModels,
extractModelIds,
fetchAWFReflect,
Expand Down
32 changes: 32 additions & 0 deletions actions/setup/js/awf_reflect.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
hasAPIProxyLocalhostAlias,
inferProviderTypeForModel,
inferWireApiForModel,
parseReflectTimeoutMs,
resolveOpenAICompatibleEndpointFromReflect,
resolveProviderEndpointFromReflect,
resolveMultiProviderFromReflect,
Expand All @@ -50,6 +51,14 @@ describe("awf_reflect.cjs", () => {
expect(DEFAULT_API_PROXY_HOST_BRIDGE).toBe("host.docker.internal");
expect(GEMINI_MODEL_NAME_PREFIX).toBe("models/");
});

it("falls back to the default reflect timeout when the environment value is invalid", () => {
expect(parseReflectTimeoutMs("")).toBe(60000);
expect(parseReflectTimeoutMs("not-a-number")).toBe(60000);
expect(parseReflectTimeoutMs("12abc")).toBe(60000);
expect(parseReflectTimeoutMs("999999999999999999999999")).toBe(60000);
expect(parseReflectTimeoutMs("1234")).toBe(1234);
});
});

describe("waitForProviderListenerReady", () => {
Expand Down Expand Up @@ -639,6 +648,29 @@ describe("awf_reflect.cjs", () => {
describe("fetchAWFReflect", () => {
afterEach(() => {
vi.unstubAllGlobals();
vi.unstubAllEnvs();
});

it("skips network requests when reflection is disabled", async () => {
const fetchMock = vi.fn();
const logs = [];
vi.stubGlobal("fetch", fetchMock);
vi.stubEnv("GH_AW_SKIP_REFLECT", "true");

await expect(
fetchAWFReflect({
reflectUrl: "http://api-proxy:10000/reflect",
outputPath: "/tmp/gh-aw-test-noop.json",
logger: msg => logs.push(msg),
})
).resolves.toEqual({
ok: false,
reflectUrl: "http://api-proxy:10000/reflect",
outputPath: "/tmp/gh-aw-test-noop.json",
reason: "disabled",
});
expect(fetchMock).not.toHaveBeenCalled();
expect(logs).toContain("awf-reflect: disabled by GH_AW_SKIP_REFLECT");
});

it("saves enriched reflect data when api-proxy returns null models for configured provider", async () => {
Expand Down
22 changes: 14 additions & 8 deletions actions/setup/js/claude_harness.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const {
} = require("./claude_harness.cjs");

const agentTempDir = "/tmp/gh-aw/agent";
const harnessChildEnv = {
...process.env,
GH_AW_HARNESS_INITIAL_DELAY_MS: "1",
GH_AW_HARNESS_MAX_DELAY_MS: "1",
GH_AW_SKIP_REFLECT: "true",
};

function makeHarnessTempDir(name) {
fs.mkdirSync(agentTempDir, { recursive: true });
Expand All @@ -46,7 +52,7 @@ function runHarnessWithStub({ stubScript, prompt = "fix the bug", extraArgs = []

const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", ...extraArgs, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: { ...process.env, ...extraEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath },
env: { ...harnessChildEnv, ...extraEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath },
encoding: "utf8",
timeout: 45000,
});
Expand Down Expand Up @@ -896,7 +902,7 @@ process.exit(0);`,

const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -928,7 +934,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -975,7 +981,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -1008,7 +1014,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: { ...process.env, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
env: { ...harnessChildEnv, CLAUDE_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -1040,7 +1046,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
CLAUDE_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_AGENT_OUTPUT: agentOutputPath,
Expand Down Expand Up @@ -1077,7 +1083,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
CLAUDE_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_MAX_RETRIES: "0",
Expand Down Expand Up @@ -1112,7 +1118,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["claude_harness.cjs", process.execPath, stubPath, "--print", "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./claude_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
CLAUDE_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_MAX_RETRIES: "0",
Expand Down
38 changes: 22 additions & 16 deletions actions/setup/js/copilot_harness.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ const {
const { detectNonRetryableHarnessGuard, buildSoftTimeoutGuard } = require("./harness_retry_guard.cjs");

const agentTempDir = "/tmp/gh-aw/agent";
const harnessChildEnv = {
...process.env,
GH_AW_HARNESS_INITIAL_DELAY_MS: "1",
GH_AW_HARNESS_MAX_DELAY_MS: "1",
GH_AW_SKIP_REFLECT: "true",
};

function makeHarnessTempDir(name) {
fs.mkdirSync(agentTempDir, { recursive: true });
Expand Down Expand Up @@ -2617,7 +2623,7 @@ process.exit(0);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -2649,7 +2655,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -2686,7 +2692,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_SAFEOUTPUTS_CLI: "true",
Expand Down Expand Up @@ -2728,7 +2734,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
encoding: "utf8",
timeout: 15000,
});
Expand Down Expand Up @@ -2760,7 +2766,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_SAFEOUTPUTS_CLI: "true" },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_SAFEOUTPUTS_CLI: "true" },
encoding: "utf8",
timeout: 15000,
});
Expand Down Expand Up @@ -2791,7 +2797,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath },
encoding: "utf8",
timeout: 15000,
});
Expand Down Expand Up @@ -2830,7 +2836,7 @@ setInterval(() => {}, 1000);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_WATCHDOG_TIMEOUT_MS: "100",
Expand Down Expand Up @@ -2870,7 +2876,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
// Override retry config to keep the test fast.
Expand Down Expand Up @@ -2914,7 +2920,7 @@ setInterval(() => {}, 1000);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_WATCHDOG_TIMEOUT_MS: "100",
Expand Down Expand Up @@ -2958,7 +2964,7 @@ setInterval(() => {}, 1000);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_WATCHDOG_TIMEOUT_MS: "100",
Expand Down Expand Up @@ -2996,7 +3002,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
},
Expand Down Expand Up @@ -3043,7 +3049,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -3076,7 +3082,7 @@ process.exit(1);`,

const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: { ...process.env, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
env: { ...harnessChildEnv, COPILOT_HARNESS_STUB_CALLS: callsPath, GH_AW_SAFE_OUTPUTS: safeOutputsPath, GH_AW_AGENT_OUTPUT: agentOutputPath },
encoding: "utf8",
timeout: 10000,
});
Expand Down Expand Up @@ -3107,7 +3113,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_MAX_RETRIES: "0",
Expand Down Expand Up @@ -3144,7 +3150,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_AGENT_OUTPUT: agentOutputPath,
Expand Down Expand Up @@ -3178,7 +3184,7 @@ process.exit(1);`,
const result = spawnSync(process.execPath, ["copilot_harness.cjs", process.execPath, stubPath, "--prompt-file", promptPath], {
cwd: path.dirname(require.resolve("./copilot_harness.cjs")),
env: {
...process.env,
...harnessChildEnv,
COPILOT_HARNESS_STUB_CALLS: callsPath,
GH_AW_SAFE_OUTPUTS: safeOutputsPath,
GH_AW_HARNESS_MAX_RETRIES: "0",
Expand Down
Loading
Loading