From 221fcddb6f32c67e5691ca88abf19f876b62b421 Mon Sep 17 00:00:00 2001 From: finalerock44 <77282157+finalerock44@users.noreply.github.com> Date: Thu, 10 Sep 2026 17:30:43 +0100 Subject: [PATCH] =?UTF-8?q?feat(notices):=20expose=20platform,=20device=20?= =?UTF-8?q?and=20Maestro=20version=20to=20notice=20=E2=80=A6=20(#147)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(notices): expose platform, device and Maestro version to notice targeting; include notices in --json output --- src/commands/cloud.ts | 22 +++++++++++++++++++--- src/services/notices.service.ts | 15 +++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/commands/cloud.ts b/src/commands/cloud.ts index 04ef937..cab16dd 100644 --- a/src/commands/cloud.ts +++ b/src/commands/cloud.ts @@ -56,7 +56,7 @@ import { CompatibilityData, fetchCompatibilityData, } from '../utils/compatibility.js'; -import { renderNotices } from '../services/notices.service.js'; +import { platformFromAppFile, renderNotices } from '../services/notices.service.js'; import { resolveApiUrl } from '../utils/config-store.js'; import { downloadExpoUrl, extractTarGz, findAppBundle, isUrl } from '../utils/expo.js'; import { @@ -494,17 +494,32 @@ export const cloudCommand = defineCommand({ // returned with the compatibility data. Replaces the previously hardcoded // iOS-16 deprecation warning — that is now a seeded notice gated on the // selected iOS version below. Honours --json via out/warnOut. - renderNotices( + const visibleNotices = renderNotices( compatibilityData.notices, { + platform: platformFromAppFile(finalAppFile), ios_version: iOSVersion, + ios_device: iOSDevice, android_api_level: androidApiLevel, + android_device: androidDevice, + // Resolved (what the run will use) and requested (undefined when the + // customer relied on the default), so a notice can target either. + maestro_version: resolvedMaestroVersion, + requested_maestro_version: maestroVersion, cli_version: cliVersion, ci_provider: ciContext.provider, ci_wrapper_version: ciContext.wrapperVersion, }, { out }, ); + // --json suppresses the rendered lines, so the payload carries them instead. + const noticesForJson = visibleNotices.map((n) => ({ + slug: n.slug, + level: n.level, + title: n.title, + body: n.body, + learnMoreUrl: n.learnMoreUrl, + })); deviceValidationService.validateAndroidDevice( androidApiLevel, @@ -963,6 +978,7 @@ export const cloudCommand = defineCommand({ tags: testMetadataMap[r.test_file_name]?.tags || [], })), uploadId: results[0].test_upload_id, + notices: noticesForJson, }; if (jsonFileFlag) { @@ -1087,7 +1103,7 @@ export const cloudCommand = defineCommand({ }); } - const jsonOutput = pollingResult; + const jsonOutput = { ...pollingResult, notices: noticesForJson }; if (jsonFileFlag) { const jsonFilePath = jsonFileName || `${results[0].test_upload_id}_dcd.json`; writeJSONFile(jsonFilePath, jsonOutput, { diff --git a/src/services/notices.service.ts b/src/services/notices.service.ts index 9a8070d..ff5022f 100644 --- a/src/services/notices.service.ts +++ b/src/services/notices.service.ts @@ -131,6 +131,21 @@ function renderNotice(notice: Notice, opts: RenderNoticesOptions): void { * payload instead of printing. `opts.out` is the caller's `--json`-gated * emitter, so under `--json` nothing prints but the list is still returned. */ +/** + * Best-effort platform from the app artifact's extension, so a notice can be + * targeted at one platform (e.g. "you rely on the default Android API level") + * without firing on the other platform's runs. + */ +export function platformFromAppFile( + appFile: string | undefined, +): 'android' | 'ios' | undefined { + if (!appFile) return undefined; + const ext = appFile.split('?')[0].toLowerCase().match(/.([a-z0-9]+)$/)?.[1]; + if (ext === 'apk' || ext === 'aab') return 'android'; + if (ext === 'zip' || ext === 'app' || ext === 'ipa') return 'ios'; + return undefined; +} + export function renderNotices( notices: Notice[] | undefined, ctx: NoticeContext,