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
13 changes: 11 additions & 2 deletions src/commands/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { jsonFlag } from '../config/flags/output.flags.js';
import {
type DownloadResult,
ReportDownloadService,
reportFormatDeprecation,
} from '../services/report-download.service.js';
import { resolveAuth } from '../utils/auth.js';
import { CliError, logger, validateEnum } from '../utils/cli.js';
import { resolveApiUrl } from '../utils/config-store.js';
import { ui } from '../utils/ui.js';

const DOWNLOAD_OPTIONS = ['ALL', 'FAILED'] as const;
const REPORT_OPTIONS = ['allure', 'html', 'html-detailed', 'junit'] as const;
Expand Down Expand Up @@ -43,12 +45,12 @@ export const artifactsCommand = defineCommand({
report: {
type: 'string',
description:
'Download a test report in the specified format (options: allure, html, html-detailed, junit)',
'Download a test report in the specified format (options: junit, html, html-detailed, allure). allure and html-detailed are deprecated and will be removed on 7 December 2026.',
},
'allure-path': {
type: 'string',
description:
'Custom file path for downloaded Allure report (default: ./report.html)',
'Custom file path for downloaded Allure report (default: ./report.html). Deprecated: Allure reports will be removed on 7 December 2026.',
},
'html-path': {
type: 'string',
Expand Down Expand Up @@ -85,6 +87,13 @@ export const artifactsCommand = defineCommand({
// Success lines are dropped under --json; warnings stay on stderr.
const log = json ? undefined : (m: string) => logger.log(m);

// This command fetches no notices and submits nothing, so neither the
// deprecation notice nor the API's submit warning reaches it.
const deprecation = reportFormatDeprecation(report);
if (deprecation) {
process.stderr.write(`${ui.deprecation(deprecation)}\n`);
}

const auth = await resolveAuth({ apiKeyFlag });

if (downloadArtifacts && report) {
Expand Down
3 changes: 3 additions & 0 deletions src/commands/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ export const cloudCommand = defineCommand({
// As requested ('default' when not passed), not as the API resolves
// it: the gpu1 retirement notice is aimed at runs that ask for gpu1.
runner_type: runnerType,
// As passed (undefined without --report): the allure and
// html-detailed retirement notices are gated on it.
report,
cli_version: cliVersion,
ci_provider: ciContext.provider,
ci_wrapper_version: ciContext.wrapperVersion,
Expand Down
4 changes: 2 additions & 2 deletions src/config/flags/output.flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const outputFlags = {
'allure-path': {
type: 'string',
description:
'Custom file path for downloaded Allure report (requires --report allure, default: ./report.html)',
'Custom file path for downloaded Allure report (requires --report allure, default: ./report.html). Deprecated: Allure reports will be removed on 7 December 2026.',
},
'html-path': {
type: 'string',
Expand Down Expand Up @@ -79,6 +79,6 @@ export const outputFlags = {
type: 'string',
alias: ['format'],
description:
'Generate and download test reports in the specified format (options: allure, junit, html, html-detailed). Use "allure" for a complete HTML report.',
'Generate and download test reports in the specified format (options: junit, html, html-detailed, allure). allure and html-detailed are deprecated and will be removed on 7 December 2026: from that date html-detailed gives the standard HTML report and allure gives none, and runs still run.',
},
} as const satisfies ArgsDef;
4 changes: 3 additions & 1 deletion src/mcp/tools/download-artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export function registerDownloadArtifacts(server: McpServer): void {
report: z
.enum(['junit', 'allure', 'html'])
.optional()
.describe('Also download a formatted report of this type'),
.describe(
'Also download a formatted report of this type. allure is deprecated and will be removed on 7 December 2026; prefer junit or html.',
),
reportPath: z
.string()
.optional()
Expand Down
24 changes: 24 additions & 0 deletions src/services/report-download.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ export interface ReportDownloadOptions extends DownloadOptions {
reportType: 'allure' | 'html' | 'html-detailed' | 'junit';
}

/**
* The report formats retired on 7 December 2026, and what each one
* does from that date. `dcd cloud` users are told by the API's submit message
* and a notice; `dcd artifacts` fetches neither, so it prints this itself.
* The values stay valid after the date: a run that asks for one must not fail.
*/
export function reportFormatDeprecation(
report: string | undefined,
): string | undefined {
switch (report) {
case 'allure': {
return '--report allure is deprecated and will be removed on 7 December 2026. From that date runs get no Allure report. Use --report html or --report junit instead.';
}

case 'html-detailed': {
return '--report html-detailed is deprecated and will be removed on 7 December 2026. From that date it gives the standard HTML report. Use --report html instead.';
}

default: {
return undefined;
}
}
}

/** What one download attempt did; failures are warned about, not thrown. */
export interface DownloadResult {
error?: string;
Expand Down
26 changes: 26 additions & 0 deletions test/integration/artifacts.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,32 @@ describe('Artifacts Command Integration Tests', () => {
});
});

// This command fetches no notices, so it prints the deprecation
// itself. On stderr, before any download is tried.
describe('report format deprecation', () => {
it('warns that --report allure goes on 7 December 2026', async () => {
const { stderr } = await run(
`--upload-id ${mockUploadId} --report allure --api-key ${mockApiKey} --api-url ${DEAD_API_URL}`,
);
expect(stderr).to.include('--report allure is deprecated');
expect(stderr).to.include('7 December 2026');
});

it('warns about --report html-detailed', async () => {
const { stderr } = await run(
`--upload-id ${mockUploadId} --report html-detailed --api-key ${mockApiKey} --api-url ${DEAD_API_URL}`,
);
expect(stderr).to.include('--report html-detailed is deprecated');
});

it('says nothing for --report junit', async () => {
const { stderr } = await run(
`--upload-id ${mockUploadId} --report junit --api-key ${mockApiKey} --api-url ${DEAD_API_URL}`,
);
expect(stderr).to.not.include('deprecated');
});
});

describe('help', () => {
it('should display help with all expected flags', async () => {
const { stdout } = await exec(`${CLI} artifacts --help`, {
Expand Down
24 changes: 24 additions & 0 deletions test/unit/notices.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,27 @@ describe('notice gating on runner_type', () => {
expect(matchesRules(gpu1Gate, { platform: 'android' })).to.equal(false);
});
});

// The allure and html-detailed retirement notices (dcd
// `deprecate-report-{allure,html-detailed}-2026-12-07`) are gated on `report`
// with no min_client_version, for the same reason as gpu1: a CLI from before
// 5.12.0 has no report in its context and must evaluate the gate as false.
describe('notice gating on report', () => {
const allureGate = {
rules: [{ field: 'report', op: 'equals' as const, value: 'allure' }],
};

it('shows the notice to a run that asks for allure', () => {
expect(matchesRules(allureGate, { report: 'allure' })).to.equal(true);
});

it('hides it from runs with another format, or none', () => {
expect(matchesRules(allureGate, { report: 'junit' })).to.equal(false);
expect(matchesRules(allureGate, { report: 'html-detailed' })).to.equal(false);
expect(matchesRules(allureGate, { report: undefined })).to.equal(false);
});

it('hides it from a context with no report, as an older CLI sends', () => {
expect(matchesRules(allureGate, { platform: 'android' })).to.equal(false);
});
});
27 changes: 26 additions & 1 deletion test/unit/report-download.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import * as os from 'node:os';
import * as path from 'node:path';

import { ApiGateway } from '../../src/gateways/api-gateway.js';
import { ReportDownloadService } from '../../src/services/report-download.service.js';
import {
ReportDownloadService,
reportFormatDeprecation,
} from '../../src/services/report-download.service.js';
import type { AuthContext } from '../../src/types/domain/auth.types.js';

const TEST_AUTH: AuthContext = {
Expand Down Expand Up @@ -550,3 +553,25 @@ describe('ReportDownloadService default report paths', () => {
]);
});
});

// allure and html-detailed go on 7 December 2026. The values stay
// valid (a run that asks for one must not fail), so the only signal is this.
describe('reportFormatDeprecation', () => {
it('names the date and the replacement for allure', () => {
const message = reportFormatDeprecation('allure');
expect(message).to.include('7 December 2026');
expect(message).to.include('--report html or --report junit');
});

it('names the date and the replacement for html-detailed', () => {
const message = reportFormatDeprecation('html-detailed');
expect(message).to.include('7 December 2026');
expect(message).to.include('--report html instead');
});

it('says nothing for the formats that stay, or for no format', () => {
for (const report of ['junit', 'html', undefined]) {
expect(reportFormatDeprecation(report)).to.equal(undefined);
}
});
});
Loading