mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-08-03 17:58:30 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be7b93b190 | ||
|
|
e0cf66d1d2 | ||
|
|
44423bdec7 | ||
|
|
b00a3414fd | ||
|
|
fa7e2f0a29 |
@ -145,7 +145,7 @@ runs:
|
||||
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||
run: |
|
||||
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
||||
CLAUDE_CODE_VERSION="2.1.216"
|
||||
CLAUDE_CODE_VERSION="2.1.220"
|
||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Installation attempt $attempt..."
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"name": "@anthropic-ai/claude-code-base-action",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.216",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.220",
|
||||
"shell-quote": "^1.8.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -27,23 +27,23 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.216", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.216", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.216", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.216", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.216" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-Fl/d9yGW7Pm0aGwUNkJMvruOVobleYcoprGJqufdP4J9W2XL/2xKXqanY8KqUdfySRMI2N46rY4/lryq5SfgFg=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.220", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.220", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.220", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.220", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.220" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-glc7SdwPkOkLw8oxwLo9PKTdLJGqW/PIR4urWXFoRtX9YllwozsEVc5Tc1+EvLSkfrsxPJqQWqOgpjUOQXf1oA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.216", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TWtTUabXj+AMOBMknSe3EVQb/7qucw5pHYi0HWoKoixQt1JE6J/CiLDBIY69OD3eQn9k1XG6LaurT239kFxLUw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.220", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7VxlbEosK7DODiOnsjoVd0DSJzbnaPrM2jelMHI0y8zx1UnLS3WC6EFUXbvy74F2sXqEznh2tzn7EKWInaRN6Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.216", "", { "os": "darwin", "cpu": "x64" }, "sha512-tULuvqhJUwGTj71Uln4uPumm0la4EO1XydFbISTbdc1BsuvGx3FOtFcVs0Qg3dhNBFAoU/iYVTO/B1rImeJZig=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.220", "", { "os": "darwin", "cpu": "x64" }, "sha512-X9RwDsSmbF6ultKZroaip+DL8WRgC64gHbrAwrRlAFSPNZV7zmJyP2ur8rW7KrxqmtuehdMMkw8+SAC/6hD2PA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.216", "", { "os": "linux", "cpu": "arm64" }, "sha512-kX6x0otwOuA1zIeEiuAKQqOk2TUAOkDUzm4MmLoQHOM2xWvr/pc+xY4DZxaX4ef9dUFQuHA4r5Vtbk7tPTL7Bw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.220", "", { "os": "linux", "cpu": "arm64" }, "sha512-WkROPwWskqhKR9XgnmseHQ6rLi9zM9qt57IWoToIjL/eXOqDWipp7JXZ1L5ud+LrA42dunHPZfBwD/vXZ+A7LA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.216", "", { "os": "linux", "cpu": "arm64" }, "sha512-sisWj2nhQolKu6aGWnu+I7d8EU6/m5J3G6bGfR7YvZ1wkQTQZAX3aOq6DVLIfCaZSH22wtqyuXhE/gn5eU1syw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.220", "", { "os": "linux", "cpu": "arm64" }, "sha512-OHoZOZ8Cf2TBr6oXIXPwyvUxj9jrq2w8E4poA8dMpacXszcPSPiCQCMuuOh4aWJzfeJE1+TtWxhKMVb2csXyZQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.216", "", { "os": "linux", "cpu": "x64" }, "sha512-WbJTLQafcYw9wl+gh/YpepdbWsOoJS7JZQA0obpzBBWzpx9PqPbjUDXDfCDrtzDI2fmr3Nz0//vC12bvlMZd8g=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.220", "", { "os": "linux", "cpu": "x64" }, "sha512-tkTJFnpR9VifvWX2fmkCAPkT6+8Wk/gVu8B5jsVekKZPiZoWRHmMXO30BnZn+f0TZhgYP+82PSX3S8crH1kn+w=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.216", "", { "os": "linux", "cpu": "x64" }, "sha512-3Ts2Ab6oEGG2r+epmWc6lOR1ahECjvH4y+lPb32qLKMdYxT2PU6TAc4Z/USLLuqDpgw3nSb8xuiYEH77rv88fw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.220", "", { "os": "linux", "cpu": "x64" }, "sha512-K+FWj+LcGhC1Z7wqeWoLxm1iemcba5xKpLLFVwYm4V6HyMx3ruYd/2r2TiQtjT+JWeNFWIys0ScHiItR6vWAiA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.216", "", { "os": "win32", "cpu": "arm64" }, "sha512-xGB8rKatsXl9enqmPXixYNvdjGh1AL2jFmMRC9lR9M1prf2FGl7dkpdHz8YyItKAciRwpPIfts/9dSGhxlgT3A=="],
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.220", "", { "os": "win32", "cpu": "arm64" }, "sha512-rIwgq0UwQExWl6KrHUyC4w5KwpL9l6nd95aUTx6RitexaAuEw//xtfTVLnuE4hDDQZFkzEwpdKc3nxDWoGcUbA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.216", "", { "os": "win32", "cpu": "x64" }, "sha512-cGbBgoKNzB6wRL6bMOwM2iJEG/wx0B6FCblmKF+ZAYKcj5oNi3jM6oQsnPJbKfrzVKrip3YEpXxopQ9O30LrSg=="],
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.220", "", { "os": "win32", "cpu": "x64" }, "sha512-MuOuXhbr66HlGaWXD2f3w0k2PsvmnbkwcUZ0dAe2poFLdl72GC2dapwwOBefxm9QmoNqk9+jmv/dSKGOVWyvLw=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.93.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-q9vaSZQVFx6B/gPxetGYfLXSJD5v0sOmh0OpZDq7yCrTSA+Rscvrtyol7JJTW40wEpQB4U1B4JXzxQitbQ3CAA=="],
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.216",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.220",
|
||||
"shell-quote": "^1.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -75,7 +75,8 @@ async function run() {
|
||||
core.setOutput("conclusion", "failure");
|
||||
process.exit(1);
|
||||
} finally {
|
||||
// Stop refreshing the workload identity token file so the process can exit
|
||||
// Stop refreshing the workload identity token file (so the process can
|
||||
// exit) and delete the token material so it doesn't outlive this step
|
||||
workloadIdentity?.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
*/
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import { mkdirSync, writeFileSync } from "fs";
|
||||
import { createHash } from "crypto";
|
||||
import { mkdirSync, rmSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { retryWithBackoff } from "./retry";
|
||||
|
||||
@ -50,6 +51,63 @@ async function fetchIdentityToken(audience: string) {
|
||||
return retryWithBackoff(() => core.getIDToken(audience));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a profile config that switches federation resolution to the
|
||||
* file-backed path. Resolving federation through a profile (rather than bare
|
||||
* env vars) enables the SDK's on-disk credentials cache, so the several
|
||||
* `claude` processes the action spawns (plugin installs, main query) share
|
||||
* one exchanged access token instead of each re-exchanging the single-use
|
||||
* GitHub OIDC token, which fails with 401 (`jti_reused`).
|
||||
*
|
||||
* The profile is intentionally minimal: the SDK gap-fills the federation
|
||||
* fields (rule, organization, identity-token file, service account, base URL)
|
||||
* from the ANTHROPIC_* env vars the action already exports, so the file only
|
||||
* needs to exist to turn the cache on.
|
||||
*
|
||||
* The config dir name embeds a fingerprint of the federation inputs. The
|
||||
* SDK's cache reuses a token on `expires_at` alone, with no record of the
|
||||
* config that minted it, and the token's scope is bound at mint time — so a
|
||||
* later action step in the same job (RUNNER_TEMP is per-job) with different
|
||||
* federation inputs must land in a different dir or it would silently reuse
|
||||
* the first step's token.
|
||||
*
|
||||
* Sharing the cache is only safe while the action spawns its `claude`
|
||||
* subprocesses sequentially: the SDK cache is not cross-process serialized,
|
||||
* and concurrent cache misses would each re-exchange the same single-use
|
||||
* identity token. Parallelizing the plugin installs would reintroduce the
|
||||
* `jti_reused` failures.
|
||||
*/
|
||||
function writeFederationProfile(baseDir: string): string {
|
||||
// Every input that changes which credential the exchange mints must be in
|
||||
// here; service_account_id and scope are sent in the exchange request body.
|
||||
const fingerprint = createHash("sha256")
|
||||
.update(
|
||||
JSON.stringify([
|
||||
process.env.ANTHROPIC_FEDERATION_RULE_ID?.trim() ?? "",
|
||||
process.env.ANTHROPIC_ORGANIZATION_ID?.trim() ?? "",
|
||||
process.env.ANTHROPIC_SERVICE_ACCOUNT_ID?.trim() ?? "",
|
||||
process.env.ANTHROPIC_WORKSPACE_ID?.trim() ?? "",
|
||||
process.env.ANTHROPIC_BASE_URL?.trim() ?? "",
|
||||
process.env.ANTHROPIC_SCOPE?.trim() ?? "",
|
||||
]),
|
||||
)
|
||||
.digest("hex")
|
||||
.slice(0, 16);
|
||||
const configDir = join(baseDir, `config-${fingerprint}`);
|
||||
|
||||
mkdirSync(join(configDir, "configs"), { recursive: true, mode: 0o700 });
|
||||
writeFileSync(
|
||||
join(configDir, "configs", "default.json"),
|
||||
JSON.stringify(
|
||||
{ version: "1.0", authentication: { type: "oidc_federation" } },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
{ mode: 0o600 },
|
||||
);
|
||||
return configDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a GitHub Actions OIDC token, writes it to a file in RUNNER_TEMP,
|
||||
* exports ANTHROPIC_IDENTITY_TOKEN_FILE, and starts a background refresh so
|
||||
@ -57,7 +115,8 @@ async function fetchIdentityToken(audience: string) {
|
||||
*
|
||||
* Returns undefined when federation is not configured or is shadowed by a
|
||||
* higher-precedence credential. Callers must invoke stop() when execution
|
||||
* finishes.
|
||||
* finishes; it also deletes the identity token and any cached exchanged
|
||||
* credential.
|
||||
*/
|
||||
export async function setupWorkloadIdentity(): Promise<
|
||||
WorkloadIdentityHandle | undefined
|
||||
@ -101,6 +160,17 @@ export async function setupWorkloadIdentity(): Promise<
|
||||
}
|
||||
|
||||
process.env.ANTHROPIC_IDENTITY_TOKEN_FILE = tokenFile;
|
||||
if (
|
||||
process.env.ANTHROPIC_CONFIG_DIR?.trim() ||
|
||||
process.env.ANTHROPIC_PROFILE?.trim()
|
||||
) {
|
||||
core.warning(
|
||||
"ANTHROPIC_CONFIG_DIR or ANTHROPIC_PROFILE is already set, so the action will not write its own federation profile. Credential caching across the spawned Claude processes follows the existing profile configuration.",
|
||||
);
|
||||
} else {
|
||||
process.env.ANTHROPIC_CONFIG_DIR = writeFederationProfile(tokenDir);
|
||||
process.env.ANTHROPIC_PROFILE = "default";
|
||||
}
|
||||
console.log(
|
||||
`Workload identity federation configured (rule: ${process.env.ANTHROPIC_FEDERATION_RULE_ID}, identity token file: ${tokenFile})`,
|
||||
);
|
||||
@ -115,6 +185,12 @@ export async function setupWorkloadIdentity(): Promise<
|
||||
|
||||
return {
|
||||
tokenFile,
|
||||
stop: () => clearInterval(refreshInterval),
|
||||
stop: () => {
|
||||
clearInterval(refreshInterval);
|
||||
// RUNNER_TEMP is per-job, not per-step: remove the identity token, the
|
||||
// profile, and the cached exchanged credential so they don't outlive
|
||||
// this step.
|
||||
rmSync(tokenDir, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,7 +2,14 @@
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test";
|
||||
import * as core from "@actions/core";
|
||||
import { existsSync, mkdtempSync, readFileSync, rmSync, statSync } from "fs";
|
||||
import {
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
} from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import {
|
||||
@ -27,6 +34,12 @@ describe("workload identity federation", () => {
|
||||
delete process.env.ANTHROPIC_ORGANIZATION_ID;
|
||||
delete process.env.ANTHROPIC_OIDC_AUDIENCE;
|
||||
delete process.env.ANTHROPIC_IDENTITY_TOKEN_FILE;
|
||||
delete process.env.ANTHROPIC_SERVICE_ACCOUNT_ID;
|
||||
delete process.env.ANTHROPIC_WORKSPACE_ID;
|
||||
delete process.env.ANTHROPIC_BASE_URL;
|
||||
delete process.env.ANTHROPIC_SCOPE;
|
||||
delete process.env.ANTHROPIC_CONFIG_DIR;
|
||||
delete process.env.ANTHROPIC_PROFILE;
|
||||
|
||||
getIDTokenSpy = spyOn(core, "getIDToken").mockResolvedValue(
|
||||
"test-identity-token",
|
||||
@ -123,5 +136,123 @@ describe("workload identity federation", () => {
|
||||
handle?.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("writes a minimal federation profile and selects it", async () => {
|
||||
process.env.ANTHROPIC_FEDERATION_RULE_ID = "fdrl_test";
|
||||
process.env.ANTHROPIC_ORGANIZATION_ID =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
process.env.ANTHROPIC_SERVICE_ACCOUNT_ID = "svac_test";
|
||||
process.env.ANTHROPIC_WORKSPACE_ID = "wrkspc_test";
|
||||
|
||||
const handle = await setupWorkloadIdentity();
|
||||
try {
|
||||
const configDir = process.env.ANTHROPIC_CONFIG_DIR;
|
||||
expect(configDir).toBeDefined();
|
||||
expect(
|
||||
configDir!.startsWith(
|
||||
join(tempDir, "claude-workload-identity", "config-"),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(process.env.ANTHROPIC_PROFILE).toBe("default");
|
||||
|
||||
const profilePath = join(configDir!, "configs", "default.json");
|
||||
expect(statSync(profilePath).mode & 0o777).toBe(0o600);
|
||||
// Minimal on purpose: the SDK gap-fills the federation fields from
|
||||
// the ANTHROPIC_* env vars the action exports.
|
||||
expect(JSON.parse(readFileSync(profilePath, "utf-8"))).toEqual({
|
||||
version: "1.0",
|
||||
authentication: { type: "oidc_federation" },
|
||||
});
|
||||
} finally {
|
||||
handle?.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("derives the config dir from the federation inputs", async () => {
|
||||
process.env.ANTHROPIC_FEDERATION_RULE_ID = "fdrl_test";
|
||||
process.env.ANTHROPIC_ORGANIZATION_ID =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
process.env.ANTHROPIC_WORKSPACE_ID = "wrkspc_a";
|
||||
|
||||
(await setupWorkloadIdentity())?.stop();
|
||||
const firstConfigDir = process.env.ANTHROPIC_CONFIG_DIR;
|
||||
expect(firstConfigDir).toBeDefined();
|
||||
|
||||
// A later step in the same job with a different workspace must not
|
||||
// share the first step's credentials cache.
|
||||
delete process.env.ANTHROPIC_CONFIG_DIR;
|
||||
delete process.env.ANTHROPIC_PROFILE;
|
||||
process.env.ANTHROPIC_WORKSPACE_ID = "wrkspc_b";
|
||||
|
||||
(await setupWorkloadIdentity())?.stop();
|
||||
const secondConfigDir = process.env.ANTHROPIC_CONFIG_DIR;
|
||||
expect(secondConfigDir).toBeDefined();
|
||||
expect(secondConfigDir).not.toBe(firstConfigDir);
|
||||
|
||||
// Same inputs land in the same dir, so an unchanged config can still
|
||||
// reuse a cached token.
|
||||
delete process.env.ANTHROPIC_CONFIG_DIR;
|
||||
delete process.env.ANTHROPIC_PROFILE;
|
||||
|
||||
(await setupWorkloadIdentity())?.stop();
|
||||
expect(process.env.ANTHROPIC_CONFIG_DIR).toBe(secondConfigDir!);
|
||||
});
|
||||
|
||||
test("does not overwrite an operator-set ANTHROPIC_PROFILE", async () => {
|
||||
process.env.ANTHROPIC_FEDERATION_RULE_ID = "fdrl_test";
|
||||
process.env.ANTHROPIC_ORGANIZATION_ID =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
process.env.ANTHROPIC_PROFILE = "operator";
|
||||
|
||||
const handle = await setupWorkloadIdentity();
|
||||
try {
|
||||
expect(process.env.ANTHROPIC_PROFILE).toBe("operator");
|
||||
expect(process.env.ANTHROPIC_CONFIG_DIR).toBeUndefined();
|
||||
expect(warningSpy).toHaveBeenCalled();
|
||||
|
||||
const entries = readdirSync(join(tempDir, "claude-workload-identity"));
|
||||
expect(entries.filter((e) => e.startsWith("config-"))).toEqual([]);
|
||||
|
||||
// The identity token file is still provisioned for the operator's
|
||||
// profile (or the env-var fallback) to consume.
|
||||
expect(process.env.ANTHROPIC_IDENTITY_TOKEN_FILE).toBe(
|
||||
handle!.tokenFile,
|
||||
);
|
||||
} finally {
|
||||
handle?.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("does not overwrite an operator-set ANTHROPIC_CONFIG_DIR", async () => {
|
||||
process.env.ANTHROPIC_FEDERATION_RULE_ID = "fdrl_test";
|
||||
process.env.ANTHROPIC_ORGANIZATION_ID =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
const operatorConfigDir = join(tempDir, "operator-config");
|
||||
process.env.ANTHROPIC_CONFIG_DIR = operatorConfigDir;
|
||||
|
||||
const handle = await setupWorkloadIdentity();
|
||||
try {
|
||||
expect(process.env.ANTHROPIC_CONFIG_DIR).toBe(operatorConfigDir);
|
||||
expect(process.env.ANTHROPIC_PROFILE).toBeUndefined();
|
||||
expect(warningSpy).toHaveBeenCalled();
|
||||
} finally {
|
||||
handle?.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("stop removes the identity token and credential cache", async () => {
|
||||
process.env.ANTHROPIC_FEDERATION_RULE_ID = "fdrl_test";
|
||||
process.env.ANTHROPIC_ORGANIZATION_ID =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
|
||||
const handle = await setupWorkloadIdentity();
|
||||
const tokenDir = join(tempDir, "claude-workload-identity");
|
||||
expect(existsSync(handle!.tokenFile)).toBe(true);
|
||||
expect(existsSync(process.env.ANTHROPIC_CONFIG_DIR!)).toBe(true);
|
||||
|
||||
handle!.stop();
|
||||
|
||||
expect(existsSync(tokenDir)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
20
bun.lock
20
bun.lock
@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.216",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.220",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
@ -37,23 +37,23 @@
|
||||
|
||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.216", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.216", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.216", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.216", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.216", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.216" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-Fl/d9yGW7Pm0aGwUNkJMvruOVobleYcoprGJqufdP4J9W2XL/2xKXqanY8KqUdfySRMI2N46rY4/lryq5SfgFg=="],
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.3.220", "", { "optionalDependencies": { "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.220", "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.220", "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.220", "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.220", "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.220" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", "@modelcontextprotocol/sdk": "^1.29.0", "zod": "^4.0.0" } }, "sha512-glc7SdwPkOkLw8oxwLo9PKTdLJGqW/PIR4urWXFoRtX9YllwozsEVc5Tc1+EvLSkfrsxPJqQWqOgpjUOQXf1oA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.216", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TWtTUabXj+AMOBMknSe3EVQb/7qucw5pHYi0HWoKoixQt1JE6J/CiLDBIY69OD3eQn9k1XG6LaurT239kFxLUw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": ["@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.220", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7VxlbEosK7DODiOnsjoVd0DSJzbnaPrM2jelMHI0y8zx1UnLS3WC6EFUXbvy74F2sXqEznh2tzn7EKWInaRN6Q=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.216", "", { "os": "darwin", "cpu": "x64" }, "sha512-tULuvqhJUwGTj71Uln4uPumm0la4EO1XydFbISTbdc1BsuvGx3FOtFcVs0Qg3dhNBFAoU/iYVTO/B1rImeJZig=="],
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": ["@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.220", "", { "os": "darwin", "cpu": "x64" }, "sha512-X9RwDsSmbF6ultKZroaip+DL8WRgC64gHbrAwrRlAFSPNZV7zmJyP2ur8rW7KrxqmtuehdMMkw8+SAC/6hD2PA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.216", "", { "os": "linux", "cpu": "arm64" }, "sha512-kX6x0otwOuA1zIeEiuAKQqOk2TUAOkDUzm4MmLoQHOM2xWvr/pc+xY4DZxaX4ef9dUFQuHA4r5Vtbk7tPTL7Bw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": ["@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.220", "", { "os": "linux", "cpu": "arm64" }, "sha512-WkROPwWskqhKR9XgnmseHQ6rLi9zM9qt57IWoToIjL/eXOqDWipp7JXZ1L5ud+LrA42dunHPZfBwD/vXZ+A7LA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.216", "", { "os": "linux", "cpu": "arm64" }, "sha512-sisWj2nhQolKu6aGWnu+I7d8EU6/m5J3G6bGfR7YvZ1wkQTQZAX3aOq6DVLIfCaZSH22wtqyuXhE/gn5eU1syw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": ["@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.220", "", { "os": "linux", "cpu": "arm64" }, "sha512-OHoZOZ8Cf2TBr6oXIXPwyvUxj9jrq2w8E4poA8dMpacXszcPSPiCQCMuuOh4aWJzfeJE1+TtWxhKMVb2csXyZQ=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.216", "", { "os": "linux", "cpu": "x64" }, "sha512-WbJTLQafcYw9wl+gh/YpepdbWsOoJS7JZQA0obpzBBWzpx9PqPbjUDXDfCDrtzDI2fmr3Nz0//vC12bvlMZd8g=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": ["@anthropic-ai/claude-agent-sdk-linux-x64@0.3.220", "", { "os": "linux", "cpu": "x64" }, "sha512-tkTJFnpR9VifvWX2fmkCAPkT6+8Wk/gVu8B5jsVekKZPiZoWRHmMXO30BnZn+f0TZhgYP+82PSX3S8crH1kn+w=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.216", "", { "os": "linux", "cpu": "x64" }, "sha512-3Ts2Ab6oEGG2r+epmWc6lOR1ahECjvH4y+lPb32qLKMdYxT2PU6TAc4Z/USLLuqDpgw3nSb8xuiYEH77rv88fw=="],
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": ["@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.220", "", { "os": "linux", "cpu": "x64" }, "sha512-K+FWj+LcGhC1Z7wqeWoLxm1iemcba5xKpLLFVwYm4V6HyMx3ruYd/2r2TiQtjT+JWeNFWIys0ScHiItR6vWAiA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.216", "", { "os": "win32", "cpu": "arm64" }, "sha512-xGB8rKatsXl9enqmPXixYNvdjGh1AL2jFmMRC9lR9M1prf2FGl7dkpdHz8YyItKAciRwpPIfts/9dSGhxlgT3A=="],
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": ["@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.220", "", { "os": "win32", "cpu": "arm64" }, "sha512-rIwgq0UwQExWl6KrHUyC4w5KwpL9l6nd95aUTx6RitexaAuEw//xtfTVLnuE4hDDQZFkzEwpdKc3nxDWoGcUbA=="],
|
||||
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.216", "", { "os": "win32", "cpu": "x64" }, "sha512-cGbBgoKNzB6wRL6bMOwM2iJEG/wx0B6FCblmKF+ZAYKcj5oNi3jM6oQsnPJbKfrzVKrip3YEpXxopQ9O30LrSg=="],
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": ["@anthropic-ai/claude-agent-sdk-win32-x64@0.3.220", "", { "os": "win32", "cpu": "x64" }, "sha512-MuOuXhbr66HlGaWXD2f3w0k2PsvmnbkwcUZ0dAe2poFLdl72GC2dapwwOBefxm9QmoNqk9+jmv/dSKGOVWyvLw=="],
|
||||
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.93.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-q9vaSZQVFx6B/gPxetGYfLXSJD5v0sOmh0OpZDq7yCrTSA+Rscvrtyol7JJTW40wEpQB4U1B4JXzxQitbQ3CAA=="],
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.216",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.220",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
|
||||
@ -75,7 +75,7 @@ async function installClaudeCode(): Promise<string> {
|
||||
return customExecutable;
|
||||
}
|
||||
|
||||
const claudeCodeVersion = "2.1.216";
|
||||
const claudeCodeVersion = "2.1.220";
|
||||
console.log(`Installing Claude Code v${claudeCodeVersion}...`);
|
||||
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
@ -318,7 +318,8 @@ async function run() {
|
||||
} finally {
|
||||
// Phase 4: Cleanup (always runs)
|
||||
|
||||
// Stop refreshing the workload identity token file
|
||||
// Stop refreshing the workload identity token file and delete the token
|
||||
// material so it doesn't outlive this step
|
||||
workloadIdentity?.stop();
|
||||
|
||||
// Update tracking comment
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user