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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- Added `xcodebuildmcp purge` to report and explicitly clean XcodeBuildMCP-managed workspace storage, including opt-in DerivedData cleanup with dry-run and confirmation safeguards.
- Added `extraArgs` as a first-class session-default value. Repo config or runtime defaults can now carry common `xcodebuild` flags (for example `-skipPackagePluginValidation` or `-disableAutomaticPackageResolution`) so they don't need repeating on every build or test call. Per-call `extraArgs` replace matching configured flags or build settings and append after non-matching defaults, while an explicit empty array (`extraArgs: []`) clears the defaults for a single call. The session management tools show, set, sync, and clear `extraArgs` alongside the other defaults.

### Fixed

- Fixed `suppressWarnings` being ignored in settled build, build-run, and test output. The flag was honored only while streaming, so warnings still reached the final MCP tool response ([#447](https://github.com/getsentry/XcodeBuildMCP/issues/447)).

## [2.6.2]

### Fixed
Expand Down
17 changes: 17 additions & 0 deletions src/utils/renderers/__tests__/cli-text-renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,23 @@ describe('cli-text-renderer', () => {
);
});

it('omits settled build warnings when suppressWarnings is set', () => {
const structuredOutput = buildOutput({
diagnostics: {
warnings: [{ message: 'unused variable "foo"', location: 'App.swift:12' }],
errors: [],
},
});

const shown = renderCliTextTranscript({ structuredOutput });
expect(shown).toContain('Warnings (1):');
expect(shown).toContain('unused variable');

const suppressed = renderCliTextTranscript({ structuredOutput, suppressWarnings: true });
expect(suppressed).not.toContain('Warnings (1):');
expect(suppressed).not.toContain('unused variable');
});

it('renders non-interactive test progress durably and deduplicates repeated counts', () => {
const output = renderCliTextTranscript({
items: [
Expand Down
37 changes: 37 additions & 0 deletions src/utils/renderers/__tests__/domain-result-text.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { describe, expect, it } from 'vitest';
import type {
BuildResultDomainResult,
LaunchResultDomainResult,
UiActionResultDomainResult,
} from '../../../types/domain-results.ts';
import { renderDomainResultTextItems } from '../domain-result-text.ts';

function buildResultWithDiagnostics(): BuildResultDomainResult {
return {
kind: 'build-result',
didError: false,
error: null,
summary: { status: 'SUCCEEDED' },
artifacts: { scheme: 'MyScheme' },
diagnostics: {
warnings: [{ message: 'unused variable "foo"', location: 'App.swift:12' }],
errors: [{ message: 'cannot find "bar" in scope', location: 'App.swift:20' }],
},
};
}

function sectionTitles(items: ReturnType<typeof renderDomainResultTextItems>): string[] {
return items.flatMap((item) => (item.type === 'section' ? [item.title] : []));
}

function uiActionResult(action: UiActionResultDomainResult['action']): UiActionResultDomainResult {
return {
kind: 'ui-action-result',
Expand Down Expand Up @@ -114,6 +133,24 @@ describe('renderDomainResultTextItems', () => {
`);
});

it('renders build warnings by default', () => {
const titles = sectionTitles(renderDomainResultTextItems(buildResultWithDiagnostics()));

expect(titles).toContain('Warnings (1):');
expect(titles).toContain('Errors (1):');
});

it('omits build warnings when suppressWarnings is set, keeping errors', () => {
const titles = sectionTitles(
renderDomainResultTextItems(buildResultWithDiagnostics(), undefined, {
suppressWarnings: true,
}),
);

expect(titles).not.toContain('Warnings (1):');
expect(titles).toContain('Errors (1):');
});

it('renders batch UI action results', () => {
expect(
renderDomainResultTextItems(
Expand Down
2 changes: 2 additions & 0 deletions src/utils/renderers/cli-text-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ function createCliTextProcessor(options: CliTextProcessorOptions): TranscriptRen
const structuredItems = renderDomainResultTextItems(
structuredOutput.result,
structuredOutput.renderHints,
{ suppressWarnings },
);
const replayItems =
sawIncomingHeaderEvent && structuredItems[0]?.type === 'header'
Expand All @@ -456,6 +457,7 @@ function createCliTextProcessor(options: CliTextProcessorOptions): TranscriptRen
const structuredItems = renderDomainResultTextItems(
structuredOutput.result,
structuredOutput.renderHints,
{ suppressWarnings },
);
const replayItems = structuredItems.filter((item) => {
if (sawIncomingHeaderEvent && item.type === 'header') return false;
Expand Down
26 changes: 20 additions & 6 deletions src/utils/renderers/domain-result-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export type TextRendererBlock =

export type TextRenderableItem = RenderItem | TextRendererBlock;

export interface DomainResultTextOptions {
suppressWarnings?: boolean;
}

const SESSION_DEFAULT_KEYS = [
'projectPath',
'workspacePath',
Expand Down Expand Up @@ -248,6 +252,7 @@ function createMarkedDiagnosticLines(

function createStandardDiagnosticSections(
diagnostics: BasicDiagnostics | TestDiagnostics | undefined,
suppressWarnings = false,
): SectionTextBlock[] {
const sections: SectionTextBlock[] = [];
if (!diagnostics) {
Expand All @@ -265,7 +270,7 @@ function createStandardDiagnosticSections(
),
);
}
if (diagnostics.warnings.length > 0) {
if (!suppressWarnings && diagnostics.warnings.length > 0) {
sections.push(
createSection(
`Warnings (${diagnostics.warnings.length}):`,
Comment thread
sentry[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -2013,8 +2018,11 @@ function createCleanResultItems(
return items;
}

function createDiagnosticSections(result: ToolDomainResult): SectionTextBlock[] {
return createStandardDiagnosticSections(getResultDiagnostics(result));
function createDiagnosticSections(
result: ToolDomainResult,
suppressWarnings: boolean,
): SectionTextBlock[] {
return createStandardDiagnosticSections(getResultDiagnostics(result), suppressWarnings);
}

function createSummaryBlock(result: ToolDomainResult): SummaryTextBlock | null {
Expand Down Expand Up @@ -2072,8 +2080,12 @@ function createTestDiscoveryProgress(

function createBuildLikeDiagnosticItems(
result: Extract<ToolDomainResult, { kind: 'build-result' | 'build-run-result' | 'test-result' }>,
suppressWarnings: boolean,
): TextRenderableItem[] {
return createStandardDiagnosticSections('diagnostics' in result ? result.diagnostics : undefined);
return createStandardDiagnosticSections(
'diagnostics' in result ? result.diagnostics : undefined,
suppressWarnings,
);
}

function createBuildRunSyntheticStepItems(
Expand Down Expand Up @@ -2576,12 +2588,14 @@ export function createNextStepsBlock(
export function renderDomainResultTextItems(
result: ToolDomainResult,
hints?: RenderHints,
options?: DomainResultTextOptions,
): TextRenderableItem[] {
const specialCaseItems = createSpecialCaseItems(result, hints);
if (specialCaseItems) {
return specialCaseItems;
}

const suppressWarnings = options?.suppressWarnings ?? false;
const items: TextRenderableItem[] = [];
if (
result.kind === 'build-result' ||
Expand All @@ -2599,7 +2613,7 @@ export function renderDomainResultTextItems(
}
}
const tailItems = createBuildLikeTailItems(result);
items.push(...createBuildLikeDiagnosticItems(result));
items.push(...createBuildLikeDiagnosticItems(result, suppressWarnings));
if (result.kind === 'build-run-result') {
items.push(...createBuildRunSyntheticStepItems(result));
}
Expand All @@ -2623,7 +2637,7 @@ export function renderDomainResultTextItems(
return items;
}

items.push(...createDiagnosticSections(result));
items.push(...createDiagnosticSections(result, suppressWarnings));
const summary = createSummaryBlock(result);
if (summary) {
items.push(summary);
Expand Down
Loading