mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-26 18:31:34 +01:00
Widen push preflight env scopes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
51a843e135
commit
6d4664801c
2 files changed
+143
-11
No files matched your search
@@ -401,7 +401,7 @@ describe("resolveExecutionRunAdapterConfig", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("fails push-capability preflight when no GitHub write credential is bound at agent or project scope", async () => {
|
||||
it("fails push-capability preflight when no GitHub write credential is bound at any supported scope", async () => {
|
||||
await expect(resolveExecutionRunAdapterConfig({
|
||||
companyId: "company-1",
|
||||
agentId: "agent-1",
|
||||
@@ -410,9 +410,10 @@ describe("resolveExecutionRunAdapterConfig", () => {
|
||||
projectEnv: { PROJECT_ONLY: "project-only" },
|
||||
requiredScopedEnvBinding: {
|
||||
keys: ["GH_TOKEN", "GITHUB_TOKEN"],
|
||||
consumerScopes: ["agent", "project"],
|
||||
consumerScopes: ["agent", "project", "environment", "routine"],
|
||||
reason: "push_write_credential_missing",
|
||||
remediation: "GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at project or agent scope.",
|
||||
remediation:
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at agent, project, environment, or routine scope.",
|
||||
},
|
||||
secretsSvc: {
|
||||
resolveAdapterConfigForRuntime: vi.fn(),
|
||||
@@ -420,18 +421,50 @@ describe("resolveExecutionRunAdapterConfig", () => {
|
||||
} as any,
|
||||
})).rejects.toMatchObject({
|
||||
code: "configuration_incomplete",
|
||||
message: expect.stringContaining("GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN"),
|
||||
message: expect.stringContaining("agent, project, environment, or routine scope"),
|
||||
resultJson: {
|
||||
configurationIncomplete: {
|
||||
reason: "push_write_credential_missing",
|
||||
requiredEnvKeys: ["GH_TOKEN", "GITHUB_TOKEN"],
|
||||
requiredScopes: ["agent", "project"],
|
||||
requiredScopes: ["agent", "project", "environment", "routine"],
|
||||
missingBindings: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("does not accept a lowercase GitHub credential binding key", async () => {
|
||||
await expect(resolveExecutionRunAdapterConfig({
|
||||
companyId: "company-1",
|
||||
agentId: "agent-1",
|
||||
issueId: "issue-1",
|
||||
environmentId: "environment-1",
|
||||
routineId: "routine-1",
|
||||
executionRunConfig: { env: { gh_token: "agent-token" } },
|
||||
environmentEnv: { gh_token: "environment-token" },
|
||||
projectEnv: { gh_token: "project-token" },
|
||||
routineEnv: { gh_token: "routine-token" },
|
||||
requiredScopedEnvBinding: {
|
||||
keys: ["GH_TOKEN", "GITHUB_TOKEN"],
|
||||
consumerScopes: ["agent", "project", "environment", "routine"],
|
||||
reason: "push_write_credential_missing",
|
||||
remediation:
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at agent, project, environment, or routine scope.",
|
||||
},
|
||||
secretsSvc: {
|
||||
resolveAdapterConfigForRuntime: vi.fn(),
|
||||
resolveEnvBindings: vi.fn(),
|
||||
} as any,
|
||||
})).rejects.toMatchObject({
|
||||
code: "configuration_incomplete",
|
||||
resultJson: {
|
||||
configurationIncomplete: {
|
||||
reason: "push_write_credential_missing",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("passes push-capability preflight when a project-scoped GitHub credential is configured", async () => {
|
||||
const resolveAdapterConfigForRuntime = vi.fn().mockResolvedValue({
|
||||
config: { env: { AGENT_ONLY: "agent-only" } },
|
||||
@@ -454,9 +487,10 @@ describe("resolveExecutionRunAdapterConfig", () => {
|
||||
projectEnv: { GH_TOKEN: { type: "plain", value: "github-token" } },
|
||||
requiredScopedEnvBinding: {
|
||||
keys: ["GH_TOKEN", "GITHUB_TOKEN"],
|
||||
consumerScopes: ["agent", "project"],
|
||||
consumerScopes: ["agent", "project", "environment", "routine"],
|
||||
reason: "push_write_credential_missing",
|
||||
remediation: "GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at project or agent scope.",
|
||||
remediation:
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at agent, project, environment, or routine scope.",
|
||||
},
|
||||
secretsSvc: {
|
||||
resolveAdapterConfigForRuntime,
|
||||
@@ -476,6 +510,98 @@ describe("resolveExecutionRunAdapterConfig", () => {
|
||||
consumerId: "project-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("passes push-capability preflight for an environment-scoped GitHub credential", async () => {
|
||||
const resolveAdapterConfigForRuntime = vi.fn().mockResolvedValue({
|
||||
config: { env: { AGENT_ONLY: "agent-only" } },
|
||||
secretKeys: new Set<string>(),
|
||||
manifest: [],
|
||||
});
|
||||
const resolveEnvBindings = vi.fn().mockResolvedValue({
|
||||
env: { GH_TOKEN: "environment-token" },
|
||||
secretKeys: new Set(["GH_TOKEN"]),
|
||||
manifest: [],
|
||||
});
|
||||
const collectMissingRuntimeBindings = vi.fn().mockResolvedValue([]);
|
||||
|
||||
const result = await resolveExecutionRunAdapterConfig({
|
||||
companyId: "company-1",
|
||||
agentId: "agent-1",
|
||||
issueId: "issue-1",
|
||||
environmentId: "environment-1",
|
||||
environmentEnv: { GH_TOKEN: { type: "plain", value: "environment-token" } },
|
||||
executionRunConfig: { env: { AGENT_ONLY: "agent-only" } },
|
||||
projectEnv: null,
|
||||
requiredScopedEnvBinding: {
|
||||
keys: ["GH_TOKEN", "GITHUB_TOKEN"],
|
||||
consumerScopes: ["agent", "project", "environment", "routine"],
|
||||
reason: "push_write_credential_missing",
|
||||
remediation:
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at agent, project, environment, or routine scope.",
|
||||
},
|
||||
secretsSvc: {
|
||||
resolveAdapterConfigForRuntime,
|
||||
resolveEnvBindings,
|
||||
collectMissingRuntimeBindings,
|
||||
} as any,
|
||||
});
|
||||
|
||||
expect(result.resolvedConfig.env).toEqual({
|
||||
GH_TOKEN: "environment-token",
|
||||
AGENT_ONLY: "agent-only",
|
||||
});
|
||||
expect(resolveEnvBindings).toHaveBeenCalledOnce();
|
||||
expect(collectMissingRuntimeBindings.mock.calls[0]?.[2]).toMatchObject({
|
||||
consumerType: "environment",
|
||||
consumerId: "environment-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("passes push-capability preflight for a routine-scoped GitHub credential", async () => {
|
||||
const resolveAdapterConfigForRuntime = vi.fn().mockResolvedValue({
|
||||
config: { env: { AGENT_ONLY: "agent-only" } },
|
||||
secretKeys: new Set<string>(),
|
||||
manifest: [],
|
||||
});
|
||||
const resolveEnvBindings = vi.fn().mockResolvedValue({
|
||||
env: { GITHUB_TOKEN: "routine-token" },
|
||||
secretKeys: new Set(["GITHUB_TOKEN"]),
|
||||
manifest: [],
|
||||
});
|
||||
const collectMissingRuntimeBindings = vi.fn().mockResolvedValue([]);
|
||||
|
||||
const result = await resolveExecutionRunAdapterConfig({
|
||||
companyId: "company-1",
|
||||
agentId: "agent-1",
|
||||
issueId: "issue-1",
|
||||
routineId: "routine-1",
|
||||
executionRunConfig: { env: { AGENT_ONLY: "agent-only" } },
|
||||
projectEnv: null,
|
||||
routineEnv: { GITHUB_TOKEN: { type: "plain", value: "routine-token" } },
|
||||
requiredScopedEnvBinding: {
|
||||
keys: ["GH_TOKEN", "GITHUB_TOKEN"],
|
||||
consumerScopes: ["agent", "project", "environment", "routine"],
|
||||
reason: "push_write_credential_missing",
|
||||
remediation:
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at agent, project, environment, or routine scope.",
|
||||
},
|
||||
secretsSvc: {
|
||||
resolveAdapterConfigForRuntime,
|
||||
resolveEnvBindings,
|
||||
collectMissingRuntimeBindings,
|
||||
} as any,
|
||||
});
|
||||
|
||||
expect(result.resolvedConfig.env).toEqual({
|
||||
AGENT_ONLY: "agent-only",
|
||||
GITHUB_TOKEN: "routine-token",
|
||||
});
|
||||
expect(resolveEnvBindings).toHaveBeenCalledOnce();
|
||||
expect(collectMissingRuntimeBindings.mock.calls[1]?.[2]).toMatchObject({
|
||||
consumerType: "routine",
|
||||
consumerId: "routine-1",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveExecutionRunAdapterConfig codex_local credential pre-dispatch gate", () => {
|
||||
|
||||
@@ -913,7 +913,7 @@ export async function resolveExecutionRunAdapterConfig(input: {
|
||||
trustPreset?: TrustPresetResolution;
|
||||
requiredScopedEnvBinding?: {
|
||||
keys: string[];
|
||||
consumerScopes: Array<"agent" | "project">;
|
||||
consumerScopes: Array<"agent" | "project" | "environment" | "routine">;
|
||||
reason: string;
|
||||
remediation: string;
|
||||
};
|
||||
@@ -940,6 +940,12 @@ export async function resolveExecutionRunAdapterConfig(input: {
|
||||
) || (
|
||||
requiredScopedEnvBinding.consumerScopes.includes("project")
|
||||
&& isConfiguredEnvBindingValue(projectEnv?.[key])
|
||||
) || (
|
||||
requiredScopedEnvBinding.consumerScopes.includes("environment")
|
||||
&& isConfiguredEnvBindingValue(environmentEnv?.[key])
|
||||
) || (
|
||||
requiredScopedEnvBinding.consumerScopes.includes("routine")
|
||||
&& isConfiguredEnvBindingValue(routineEnv?.[key])
|
||||
))
|
||||
: false;
|
||||
if (requiredScopedEnvBinding && !requiredScopedBindingsConfigured) {
|
||||
@@ -1033,7 +1039,7 @@ export async function resolveExecutionRunAdapterConfig(input: {
|
||||
const requiredEnvKeys = new Set(requiredScopedEnvBinding.keys);
|
||||
const requiredScopes = new Set(requiredScopedEnvBinding.consumerScopes);
|
||||
const requiredMissingBindings = missingBindings.filter((binding) =>
|
||||
requiredScopes.has(binding.consumerType as "agent" | "project")
|
||||
requiredScopes.has(binding.consumerType as "agent" | "project" | "environment" | "routine")
|
||||
&& requiredEnvKeys.has(binding.envKey),
|
||||
);
|
||||
if (requiredMissingBindings.length > 0) {
|
||||
@@ -14596,10 +14602,10 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
requiredScopedEnvBinding: pushCapabilityPreflightRequired
|
||||
? {
|
||||
keys: [...PUSH_CAPABILITY_ENV_KEYS],
|
||||
consumerScopes: ["agent", "project"],
|
||||
consumerScopes: ["agent", "project", "environment", "routine"],
|
||||
reason: "push_write_credential_missing",
|
||||
remediation:
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at project or agent scope.",
|
||||
"GitHub PR workflow requires GH_TOKEN or GITHUB_TOKEN bound at agent, project, environment, or routine scope.",
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user