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
23 changes: 12 additions & 11 deletions src/cli/commands/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,17 +446,17 @@ describe("generateCommand", () => {
mockCwd.mockRestore();
});

it("should handle errors in ignore processing gracefully", async () => {
it("should fail the command on an ignore error instead of reporting success", async () => {
vi.mocked(IgnoreProcessor).mockImplementation(function () {
throw new Error("Test error");
});
const options: GenerateOptions = {};

// Should not throw, errors are caught and processing continues
await generateCommand(mockLogger, options);

// Should still complete without error
expect(mockLogger.info).toHaveBeenCalledWith("✓ All files are up to date (ignore)");
// A swallowed ignore error used to end with "All files are up to date"
// while nothing was written — the fail-open direction for the feature
// that keeps secrets away from AI tools (see #2551).
await expect(generateCommand(mockLogger, options)).rejects.toThrow("Test error");
expect(mockLogger.info).not.toHaveBeenCalledWith("✓ All files are up to date (ignore)");
});

it("should skip ignore files when no rulesync files found", async () => {
Expand Down Expand Up @@ -1225,17 +1225,18 @@ describe("generateCommand", () => {
return mockRulesProcessor as any;
});

// Set up ignore processor to throw an error (errors are caught and ignored in lib)
// Set up ignore processor to throw an error — a feature error now
// fails the whole run instead of being silently ignored (#2551).
// Ignore is the first step in GENERATION_STEP_GRAPH, so the rules step
// never runs and no success banner is emitted.
vi.mocked(IgnoreProcessor).mockImplementation(function () {
throw new Error("Ignore error");
});

const options: GenerateOptions = {};

await generateCommand(mockLogger, options);

expect(mockLogger.success).toHaveBeenCalledWith("Written 2 rules");
expect(mockLogger.success).toHaveBeenCalledWith(
await expect(generateCommand(mockLogger, options)).rejects.toThrow("Ignore error");
expect(mockLogger.success).not.toHaveBeenCalledWith(
"🎉 All done! Written 2 file(s) total (2 rules)",
);
});
Expand Down
10 changes: 6 additions & 4 deletions src/lib/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,15 +385,17 @@ describe("generate", () => {
);
});

it("should handle errors gracefully and continue", async () => {
it("should fail the run on an ignore error instead of reporting success", async () => {
// Ignore files keep secrets out of AI tools' reach; a swallowed error
// used to exit 0 "up to date" while nothing was written (see #2486 for
// the permissions counterpart).
mockConfig.getFeatures.mockReturnValue(["ignore"]);
vi.mocked(IgnoreProcessor).mockImplementation(function () {
throw new Error("Test error");
});

const result = await generate({ logger, config: mockConfig as never });

expect(result.ignoreCount).toBe(0);
await expect(generate({ logger, config: mockConfig as never })).rejects.toThrow("Test error");
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Test error"));
});

it("should skip writing when no rulesync files found", async () => {
Expand Down
7 changes: 5 additions & 2 deletions src/lib/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,10 +789,13 @@ async function generateIgnoreCore(params: {
allPaths.push(...result.paths);
if (result.hasDiff) hasDiff = true;
} catch (error) {
logger.warn(
// Ignore files are what keep secrets out of AI tools' reach — a
// silently-skipped ignore generation is the same fail-open bug the
// permissions feature had (#2486), so it fails the run the same way.
logger.error(
`Failed to generate ${toolTarget} ignore files for ${outputRoot}: ${formatError(error)}`,
);
continue;
throw error;
}
}
}
Expand Down
Loading