From c1f73672b63f841cd44dc4e154c78aa2fa6afb9a Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Fri, 26 Jun 2026 13:52:58 +0300 Subject: [PATCH 1/2] feat: introduce the `showReportMessage` command All our reporters have this nice command; however, it should collapse to an informational message about flakiness report location on CI. --- README.md | 1 + src/index.ts | 1 + src/showReportMessage.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/showReportMessage.ts diff --git a/README.md b/README.md index c49f751..a72c4de 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ Use this entry point when you need to process or manipulate reports in browser-b - **`fetchTestDurations()`** - Fetch historical test durations from Flakiness.io and return a report enriched with timings - **`showReport()`** - Start a local server and open the report in your browser - **`showReportCommand()`** - Build a shell command for opening the report later with the Flakiness CLI +- **`showReportMessage()`** - Build the message a runner prints after writing a report (CI-aware: a one-liner with the report path on CI, open-in-CLI instructions otherwise) - **`uploadReport()`** - Upload reports and attachments to Flakiness.io - **`writeReport()`** - Write reports to disk in the standard Flakiness report format diff --git a/src/index.ts b/src/index.ts index cddfa34..134b33c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,5 +11,6 @@ export { fetchTestDurations, type FetchTestDurationsOptions } from './fetchTestD export { readReport } from './readReport.js'; export { showReport } from './showReport.js'; export { showReportCommand } from './showReportCommand.js'; +export { showReportMessage } from './showReportMessage.js'; export { uploadReport } from './uploadReport.js'; export { writeReport } from './writeReport.js'; diff --git a/src/showReportMessage.ts b/src/showReportMessage.ts new file mode 100644 index 0000000..b2319ba --- /dev/null +++ b/src/showReportMessage.ts @@ -0,0 +1,30 @@ +import path from 'node:path'; +import { styleText } from 'node:util'; +import { showReportCommand } from './showReportCommand.js'; + +/** + * Builds the human-facing message that test runners print after writing a report. + * + * In interactive environments this returns instructions for opening the report + * locally with the Flakiness CLI. On CI (detected via `process.env.CI`), where + * launching the local viewer isn't useful, it returns a one-liner pointing at the + * folder the report was written to instead. + * + * @param {string} reportFolder - Absolute or relative path to the report folder. + * + * @returns {string} A ready-to-print message. + * + * @example + * ```typescript + * console.log(showReportMessage('./flakiness-report')); + * // Interactive: "To open last Flakiness report, run:\n\n npx flakiness show" + * // On CI: "Flakiness report written to /abs/path/flakiness-report" + * ``` + */ +export function showReportMessage(reportFolder: string): string { + if (process.env.CI) + return `Flakiness report written to ${path.resolve(reportFolder)}`; + + const command = showReportCommand(reportFolder); + return `To open last Flakiness report, run:\n\n ${styleText('cyan', command)}`; +} From 81738a21dd207101d58af726d6ee3225b001ab22 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Fri, 26 Jun 2026 14:13:27 +0300 Subject: [PATCH 2/2] fixes --- src/_internalUtils.ts | 13 +++++++++++++ src/showReportMessage.ts | 9 +++++---- src/uploadReport.ts | 6 +++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/_internalUtils.ts b/src/_internalUtils.ts index 29471b6..c8724bc 100644 --- a/src/_internalUtils.ts +++ b/src/_internalUtils.ts @@ -97,6 +97,19 @@ export function sha1File(filePath: string): Promise { }); } +/** + * Whether the current process is running on CI. + * + * Virtually every CI provider (GitHub Actions, GitLab, CircleCI, …) sets the `CI` + * environment variable, usually to `true` or `1`. An explicit `false`/`0`/empty + * value is treated as an opt-out rather than as "on" — `process.env.CI` is a + * non-empty truthy string in those cases too, so a plain truthiness check is wrong. + */ +export function isCI(): boolean { + const ci = (process.env.CI ?? '').toLowerCase(); + return ci !== '' && ci !== 'false' && ci !== '0'; +} + export function randomUUIDBase62(): string { const BASE62_CHARSET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let num = BigInt('0x' + crypto.randomUUID().replace(/-/g, '')); diff --git a/src/showReportMessage.ts b/src/showReportMessage.ts index b2319ba..92ffa7d 100644 --- a/src/showReportMessage.ts +++ b/src/showReportMessage.ts @@ -1,14 +1,15 @@ import path from 'node:path'; import { styleText } from 'node:util'; +import { isCI } from './_internalUtils.js'; import { showReportCommand } from './showReportCommand.js'; /** * Builds the human-facing message that test runners print after writing a report. * * In interactive environments this returns instructions for opening the report - * locally with the Flakiness CLI. On CI (detected via `process.env.CI`), where - * launching the local viewer isn't useful, it returns a one-liner pointing at the - * folder the report was written to instead. + * locally with the Flakiness CLI. On CI (the `CI` environment variable is set to + * anything other than `false`/`0`), where launching the local viewer isn't useful, + * it returns a one-liner pointing at the folder the report was written to instead. * * @param {string} reportFolder - Absolute or relative path to the report folder. * @@ -22,7 +23,7 @@ import { showReportCommand } from './showReportCommand.js'; * ``` */ export function showReportMessage(reportFolder: string): string { - if (process.env.CI) + if (isCI()) return `Flakiness report written to ${path.resolve(reportFolder)}`; const command = showReportCommand(reportFolder); diff --git a/src/uploadReport.ts b/src/uploadReport.ts index 365ae5f..2fd82ac 100644 --- a/src/uploadReport.ts +++ b/src/uploadReport.ts @@ -3,7 +3,7 @@ import assert from 'assert'; import fs from 'fs'; import { URL } from 'url'; import { GithubOIDC } from './githubOIDC.js'; -import { compressTextAsync, getJSON, putBuffer, sha1File, sha1Text } from './_internalUtils.js'; +import { compressTextAsync, getJSON, isCI, putBuffer, sha1File, sha1Text } from './_internalUtils.js'; type ReportUploaderOptions = { flakinessEndpoint: string; @@ -246,7 +246,7 @@ export async function uploadReport( if (!flakinessAccessToken && githubOIDC) { if (!report.flakinessProject) { const reason = '`flakinessProject` is not configured to upload using Github OIDC.'; - if (process.env.CI) + if (isCI()) logger.warn(`[flakiness.io] ⚠ Skipping upload: ${reason}`); return { status: 'skipped', reason }; } @@ -265,7 +265,7 @@ export async function uploadReport( if (!flakinessAccessToken) { const reason = 'No FLAKINESS_ACCESS_TOKEN found'; - if (process.env.CI) + if (isCI()) logger.warn(`[flakiness.io] ⚠ Skipping upload: ${reason}`); return { status: 'skipped', reason }; }