@invarn/cibuild 2.0.7 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cli.cjs +688 -69
  2. package/dist/src/lib.d.ts +4 -0
  3. package/dist/src/lib.d.ts.map +1 -1
  4. package/dist/src/lib.js +3 -0
  5. package/dist/src/yaml/steps/index.d.ts.map +1 -1
  6. package/dist/src/yaml/steps/index.js +22 -0
  7. package/dist/src/yaml/steps/render-post-processor.d.ts +73 -0
  8. package/dist/src/yaml/steps/render-post-processor.d.ts.map +1 -0
  9. package/dist/src/yaml/steps/render-post-processor.js +290 -0
  10. package/dist/src/yaml/steps/render-post-processor.test.d.ts +16 -0
  11. package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +1 -0
  12. package/dist/src/yaml/steps/render-post-processor.test.js +194 -0
  13. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +66 -0
  14. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +1 -0
  15. package/dist/src/yaml/steps/ui-fidelity-preview-android.js +216 -0
  16. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts +13 -0
  17. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts.map +1 -0
  18. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +247 -0
  19. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts +99 -0
  20. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +1 -0
  21. package/dist/src/yaml/steps/ui-fidelity-preview.js +152 -0
  22. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts +11 -0
  23. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts.map +1 -0
  24. package/dist/src/yaml/steps/ui-fidelity-preview.test.js +168 -0
  25. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +92 -0
  26. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +1 -0
  27. package/dist/src/yaml/steps/ui-fidelity-render-android.js +726 -0
  28. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts +2 -0
  29. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts.map +1 -0
  30. package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +377 -0
  31. package/dist/src/yaml/steps/ui-fidelity-render.d.ts +0 -28
  32. package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +1 -1
  33. package/dist/src/yaml/steps/ui-fidelity-render.js +1 -259
  34. package/dist/src/yaml/steps/ui-fidelity-render.test.js +1 -57
  35. package/package.json +1 -1
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Local ui-fidelity render preview.
3
+ *
4
+ * Runs the EXACT same render path the runner uses -- the generated
5
+ * `ui-fidelity-render` node script in `package_source: "inputs"` mode (harness
6
+ * synthesis, asset-catalog compile, content trim, reference alignment, dimension
7
+ * reporting) -- but locally, against a SwiftPM package the caller already has on
8
+ * disk. This lets an agent eyeball the wrapper in seconds before dispatching a
9
+ * remote run, so the remote pass becomes a one-shot authoritative render rather
10
+ * than the inner loop of an iterate cycle.
11
+ *
12
+ * It is a faithful mirror, not a re-implementation: the package directory is
13
+ * archived to a temp `package.tar.gz` and fed through the same
14
+ * `generateRenderScript` output, so the target is auto-discovered, the archive
15
+ * is validated, and the produced rendered/aligned images and dimensions match
16
+ * what a remote run would produce on the same toolchain.
17
+ */
18
+ import { spawnSync } from 'node:child_process';
19
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, statSync, writeFileSync, } from 'node:fs';
20
+ import { tmpdir } from 'node:os';
21
+ import { basename, join, resolve } from 'node:path';
22
+ import { DEFAULT_RENDER_SIZE, DEFAULT_SCALE, generateRenderScript, parseRenderSize, parseScale, } from './ui-fidelity-render.js';
23
+ function runTar(args, pathPrepend) {
24
+ const env = pathPrepend
25
+ ? { ...process.env, PATH: pathPrepend + ':' + (process.env.PATH ?? '') }
26
+ : process.env;
27
+ const result = spawnSync('tar', args, { encoding: 'utf-8', env, maxBuffer: 64 * 1024 * 1024 });
28
+ if (result.status !== 0) {
29
+ const detail = (result.stderr || result.error?.message || '').toString().trim();
30
+ throw new Error(`failed to archive the package (tar exited ${result.status}): ${detail}`);
31
+ }
32
+ }
33
+ /** Resolve an artifact-relative path to absolute, or null. */
34
+ function absOrNull(artifactsDir, relative) {
35
+ return typeof relative === 'string' && relative !== '' ? resolve(artifactsDir, relative) : null;
36
+ }
37
+ /**
38
+ * Render the requested screens locally and return the produced image paths and
39
+ * dimensions. Throws only on caller errors (bad options) or when the render
40
+ * script could not run at all; per-screen and per-build render failures are
41
+ * reported in the returned result (mirroring the remote result document).
42
+ */
43
+ export function renderUiFidelityPreview(options) {
44
+ if (!options || typeof options !== 'object') {
45
+ throw new Error('renderUiFidelityPreview: options are required');
46
+ }
47
+ const hasDir = typeof options.packagePath === 'string' && options.packagePath !== '';
48
+ const hasArchive = typeof options.packageArchive === 'string' && options.packageArchive !== '';
49
+ if (hasDir === hasArchive) {
50
+ throw new Error('renderUiFidelityPreview: provide exactly one of packagePath or packageArchive');
51
+ }
52
+ if (!Array.isArray(options.screens) || options.screens.length === 0) {
53
+ throw new Error('renderUiFidelityPreview: at least one screen is required');
54
+ }
55
+ const { width, height } = parseRenderSize(options.renderSize ?? DEFAULT_RENDER_SIZE);
56
+ const scale = parseScale(options.scale ?? DEFAULT_SCALE);
57
+ const workDir = mkdtempSync(join(tmpdir(), 'ui-fidelity-preview-'));
58
+ const inputsDir = join(workDir, '.ci', 'inputs');
59
+ mkdirSync(inputsDir, { recursive: true });
60
+ // Stage references by basename and build the screens -> basename map, exactly
61
+ // as a remote caller would pass params.screens.
62
+ const screensParam = {};
63
+ for (const entry of options.screens) {
64
+ if (!entry || typeof entry.screen !== 'string' || entry.screen === '') {
65
+ throw new Error('renderUiFidelityPreview: each screen needs a non-empty screen name');
66
+ }
67
+ if (typeof entry.reference !== 'string' || entry.reference === '') {
68
+ throw new Error(`renderUiFidelityPreview: screen ${entry.screen} needs a reference path`);
69
+ }
70
+ if (!existsSync(entry.reference) || !statSync(entry.reference).isFile()) {
71
+ throw new Error(`renderUiFidelityPreview: reference for ${entry.screen} not found: ${entry.reference}`);
72
+ }
73
+ const base = basename(entry.reference);
74
+ copyFileSync(entry.reference, join(inputsDir, base));
75
+ screensParam[entry.screen] = base;
76
+ }
77
+ const archivePath = join(inputsDir, 'package.tar.gz');
78
+ if (hasArchive) {
79
+ const src = options.packageArchive;
80
+ if (!existsSync(src) || !statSync(src).isFile()) {
81
+ throw new Error(`renderUiFidelityPreview: packageArchive not found: ${src}`);
82
+ }
83
+ copyFileSync(src, archivePath);
84
+ }
85
+ else {
86
+ const dir = options.packagePath;
87
+ if (!existsSync(dir) || !statSync(dir).isDirectory()) {
88
+ throw new Error(`renderUiFidelityPreview: packagePath is not a directory: ${dir}`);
89
+ }
90
+ // Archive with Package.swift at the archive root; exclude build/VCS noise so
91
+ // the upload stays lean and matches what a careful caller would ship.
92
+ runTar([
93
+ '--exclude',
94
+ '.build',
95
+ '--exclude',
96
+ '.git',
97
+ '--exclude',
98
+ '.DS_Store',
99
+ '-czf',
100
+ archivePath,
101
+ '-C',
102
+ dir,
103
+ '.',
104
+ ], options.toolchainPath);
105
+ }
106
+ // The standard params.json the render script reads: screen -> reference basename.
107
+ writeFileSync(join(inputsDir, 'params.json'), JSON.stringify({ screens: screensParam }, null, 2) + '\n');
108
+ const artifactsDir = options.outDir ? resolve(options.outDir) : join(workDir, '.ci', 'artifacts');
109
+ mkdirSync(artifactsDir, { recursive: true });
110
+ const config = {
111
+ packagePath: null,
112
+ target: options.target ?? null,
113
+ width,
114
+ height,
115
+ scale,
116
+ packageSource: 'inputs',
117
+ };
118
+ const script = generateRenderScript(config);
119
+ const env = {
120
+ ...process.env,
121
+ CIBUILD_ARTIFACTS_DIR: artifactsDir,
122
+ };
123
+ if (options.toolchainPath) {
124
+ env.PATH = options.toolchainPath + ':' + (process.env.PATH ?? '');
125
+ }
126
+ const run = spawnSync('node', ['-e', script], {
127
+ cwd: workDir,
128
+ env,
129
+ encoding: 'utf-8',
130
+ maxBuffer: 32 * 1024 * 1024,
131
+ });
132
+ const resultPath = join(artifactsDir, 'protocol-result.json');
133
+ if (!existsSync(resultPath)) {
134
+ const detail = ((run.stdout || '') + '\n' + (run.stderr || '')).trim();
135
+ throw new Error(`renderUiFidelityPreview: render script produced no result document` +
136
+ (detail ? `:\n${detail}` : ''));
137
+ }
138
+ const raw = JSON.parse(readFileSync(resultPath, 'utf-8'));
139
+ const screens = (raw.screens || []).map((s) => ({
140
+ screen: s.screen,
141
+ status: s.status,
142
+ error: s.error ?? null,
143
+ referenceImagePath: absOrNull(artifactsDir, s.reference_image_path),
144
+ renderedImagePath: absOrNull(artifactsDir, s.rendered_image_path),
145
+ alignedImagePath: absOrNull(artifactsDir, s.aligned_image_path),
146
+ dimensions: s.dimensions ?? null,
147
+ }));
148
+ const buildError = raw.error ?? null;
149
+ const ok = buildError === null && screens.length > 0 && screens.every((s) => s.status === 'rendered');
150
+ return { packageSource: 'inputs', buildError, screens, artifactsDir, ok };
151
+ }
152
+ //# sourceMappingURL=ui-fidelity-preview.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for the local ui-fidelity render preview engine.
3
+ *
4
+ * Validation and result-shaping run with a PATH-shimmed fake `swift`/`xcrun`
5
+ * (the engine spawns the real generated render script, which spawns the
6
+ * toolchain). The CoreGraphics render itself is covered by an opt-in
7
+ * real-toolchain test, gated on CIBUILD_UI_FIDELITY_REAL_SWIFT, since the fake
8
+ * toolchain cannot exercise the actual pixel render/trim.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=ui-fidelity-preview.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-fidelity-preview.test.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-preview.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Tests for the local ui-fidelity render preview engine.
3
+ *
4
+ * Validation and result-shaping run with a PATH-shimmed fake `swift`/`xcrun`
5
+ * (the engine spawns the real generated render script, which spawns the
6
+ * toolchain). The CoreGraphics render itself is covered by an opt-in
7
+ * real-toolchain test, gated on CIBUILD_UI_FIDELITY_REAL_SWIFT, since the fake
8
+ * toolchain cannot exercise the actual pixel render/trim.
9
+ */
10
+ import { describe, expect, test, afterAll } from '@jest/globals';
11
+ import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
12
+ import { tmpdir } from 'node:os';
13
+ import { dirname, join, resolve } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { renderUiFidelityPreview } from './ui-fidelity-preview.js';
16
+ const FIXTURE_PACKAGE = resolve(dirname(fileURLToPath(import.meta.url)), '../../../test/fixtures/ui-fidelity-package');
17
+ const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
18
+ // A real, decodable 1x1 transparent PNG, used as the reference for the
19
+ // real-toolchain test so the aligned step has something to read.
20
+ const ONE_BY_ONE_PNG = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', 'base64');
21
+ const tempDirs = [];
22
+ afterAll(() => {
23
+ for (const dir of tempDirs) {
24
+ try {
25
+ rmSync(dir, { recursive: true, force: true });
26
+ }
27
+ catch {
28
+ // best effort
29
+ }
30
+ }
31
+ });
32
+ function tempDir(prefix) {
33
+ const dir = mkdtempSync(join(tmpdir(), prefix));
34
+ tempDirs.push(dir);
35
+ return dir;
36
+ }
37
+ // Compact fake toolchain: enough of `swift`/`xcrun` for the inputs-mode render
38
+ // script to discover the target, "build", "render" a PNG stub, and "trim" it.
39
+ const FAKE_SWIFT = [
40
+ '#!/bin/bash',
41
+ 'cmd="$1"; shift',
42
+ 'case "$cmd" in',
43
+ ' package)',
44
+ ' printf \'{"name":"shipped","products":[{"name":"FixtureViews","type":{"library":["automatic"]}}]}\\n\'',
45
+ ' exit 0 ;;',
46
+ ' build)',
47
+ ' while [ $# -gt 0 ]; do case "$1" in --show-bin-path) echo "$PWD/.build/debug"; exit 0 ;; *) shift ;; esac; done',
48
+ ' exit 0 ;;',
49
+ ' run)',
50
+ ' pos=()',
51
+ ' while [ $# -gt 0 ]; do case "$1" in --package-path) shift 2 ;; --skip-build) shift ;; *) pos+=("$1"); shift ;; esac; done',
52
+ ' out="${pos[1]}"',
53
+ " printf '\\x89PNG\\r\\n\\x1a\\n' > \"$out\"; printf 'fake-render' >> \"$out\"",
54
+ ' exit 0 ;;',
55
+ ' *.swift)',
56
+ ' out_png="$2"; aligned_png="$3"',
57
+ " printf '\\x89PNG\\r\\n\\x1a\\n' > \"$out_png\"; printf 'trimmed' >> \"$out_png\"",
58
+ ' if [ -n "$aligned_png" ]; then printf \'\\x89PNG\\r\\n\\x1a\\n\' > "$aligned_png"; printf \'aligned\' >> "$aligned_png"; fi',
59
+ ' echo "TRIMDIMS 320 640 160 320 400 800"',
60
+ ' exit 0 ;;',
61
+ ' *) exit 0 ;;',
62
+ 'esac',
63
+ ].join('\n');
64
+ const FAKE_XCRUN = ['#!/bin/bash', 'exit 0'].join('\n');
65
+ function fakeToolchain() {
66
+ const bin = tempDir('ui-fidelity-fakebin-');
67
+ const swiftPath = join(bin, 'swift');
68
+ writeFileSync(swiftPath, FAKE_SWIFT + '\n');
69
+ chmodSync(swiftPath, 0o755);
70
+ const xcrunPath = join(bin, 'xcrun');
71
+ writeFileSync(xcrunPath, FAKE_XCRUN + '\n');
72
+ chmodSync(xcrunPath, 0o755);
73
+ return bin;
74
+ }
75
+ function writeReference() {
76
+ const dir = tempDir('ui-fidelity-ref-');
77
+ const ref = join(dir, 'reference.png');
78
+ writeFileSync(ref, Buffer.concat([PNG_MAGIC, Buffer.from('ref')]));
79
+ return ref;
80
+ }
81
+ function isPng(filePath) {
82
+ return readFileSync(filePath).subarray(0, PNG_MAGIC.length).equals(PNG_MAGIC);
83
+ }
84
+ describe('renderUiFidelityPreview — option validation', () => {
85
+ test('requires exactly one of packagePath or packageArchive', () => {
86
+ expect(() => renderUiFidelityPreview({ screens: [] })).toThrow(/exactly one/);
87
+ expect(() => renderUiFidelityPreview({
88
+ packagePath: FIXTURE_PACKAGE,
89
+ packageArchive: '/tmp/x.tar.gz',
90
+ screens: [{ screen: 'HomeView', reference: writeReference() }],
91
+ })).toThrow(/exactly one/);
92
+ });
93
+ test('requires at least one screen', () => {
94
+ expect(() => renderUiFidelityPreview({ packagePath: FIXTURE_PACKAGE, screens: [] })).toThrow(/at least one screen/);
95
+ });
96
+ test('rejects a missing reference image', () => {
97
+ expect(() => renderUiFidelityPreview({
98
+ packagePath: FIXTURE_PACKAGE,
99
+ screens: [{ screen: 'HomeView', reference: '/no/such/reference.png' }],
100
+ })).toThrow(/reference for HomeView not found/);
101
+ });
102
+ });
103
+ describe('renderUiFidelityPreview — fake toolchain', () => {
104
+ test('archives the package, renders in inputs mode, and returns image paths + dims', () => {
105
+ const result = renderUiFidelityPreview({
106
+ packagePath: FIXTURE_PACKAGE,
107
+ screens: [{ screen: 'HomeView', reference: writeReference() }],
108
+ toolchainPath: fakeToolchain(),
109
+ });
110
+ expect(result.packageSource).toBe('inputs');
111
+ expect(result.buildError).toBeNull();
112
+ expect(result.ok).toBe(true);
113
+ expect(result.screens).toHaveLength(1);
114
+ const [screen] = result.screens;
115
+ expect(screen.screen).toBe('HomeView');
116
+ expect(screen.status).toBe('rendered');
117
+ expect(screen.error).toBeNull();
118
+ // Paths are absolute and point at real files the caller can read.
119
+ expect(screen.renderedImagePath && isPng(screen.renderedImagePath)).toBe(true);
120
+ expect(screen.alignedImagePath && isPng(screen.alignedImagePath)).toBe(true);
121
+ expect(screen.referenceImagePath && isPng(screen.referenceImagePath)).toBe(true);
122
+ expect(screen.dimensions).toEqual({
123
+ rendered_px: [320, 640],
124
+ logical_pt: [160, 320],
125
+ reference_px: [160, 320],
126
+ });
127
+ });
128
+ test('writes images under the requested outDir', () => {
129
+ const outDir = tempDir('ui-fidelity-out-');
130
+ const result = renderUiFidelityPreview({
131
+ packagePath: FIXTURE_PACKAGE,
132
+ screens: [{ screen: 'HomeView', reference: writeReference() }],
133
+ outDir,
134
+ toolchainPath: fakeToolchain(),
135
+ });
136
+ expect(result.artifactsDir).toBe(resolve(outDir));
137
+ expect(result.screens[0].renderedImagePath?.startsWith(resolve(outDir))).toBe(true);
138
+ expect(existsSync(join(outDir, 'ui-fidelity', 'rendered', 'HomeView.png'))).toBe(true);
139
+ });
140
+ });
141
+ // Opt-in: renders the fixture with the real Swift toolchain and asserts the
142
+ // content trim actually crops below the forced device canvas. Requires macOS
143
+ // with Xcode; skipped unless explicitly requested.
144
+ describe('renderUiFidelityPreview — real toolchain', () => {
145
+ const realTest = process.env.CIBUILD_UI_FIDELITY_REAL_SWIFT === '1' ? test : test.skip;
146
+ realTest('renders the fixture and trims the filled canvas down to its content', () => {
147
+ const refDir = tempDir('ui-fidelity-realref-');
148
+ const reference = join(refDir, 'reference.png');
149
+ writeFileSync(reference, ONE_BY_ONE_PNG);
150
+ const result = renderUiFidelityPreview({
151
+ packagePath: FIXTURE_PACKAGE,
152
+ screens: [{ screen: 'HomeView', reference }],
153
+ renderSize: '393x852',
154
+ scale: 2,
155
+ });
156
+ expect(result.ok).toBe(true);
157
+ const [screen] = result.screens;
158
+ expect(screen.status).toBe('rendered');
159
+ expect(screen.renderedImagePath && isPng(screen.renderedImagePath)).toBe(true);
160
+ // HomeView fills the canvas with a uniform translucent background and a
161
+ // little text; the content trim must crop well below the 786x1704 (@2x)
162
+ // device canvas to the text region.
163
+ const [rw, rh] = screen.dimensions.rendered_px;
164
+ expect(rw).toBeLessThan(786);
165
+ expect(rh).toBeLessThan(1704);
166
+ }, 600_000);
167
+ });
168
+ //# sourceMappingURL=ui-fidelity-preview.test.js.map
@@ -0,0 +1,92 @@
1
+ /**
2
+ * ui-fidelity-render-android step implementation.
3
+ *
4
+ * Renders parameterless Jetpack Compose wrappers from a self-contained Gradle
5
+ * project to PNGs via Roborazzi + Robolectric on the JVM — no device, no
6
+ * emulator — on a macOS runner. The step returns a self-contained node script
7
+ * that, at runtime:
8
+ *
9
+ * 1. Reads `.ci/inputs/params.json`
10
+ * ({ "screens": { "<ScreenName>": "<referenceFileBasename>" } });
11
+ * reference images live at `.ci/inputs/<basename>`.
12
+ * 2. Extracts and validates the shipped Gradle project shipped as
13
+ * `.ci/inputs/package.tar.gz` (single project root carrying a settings
14
+ * script, bounded extracted size, safe member paths).
15
+ * 3. Runs the project's Roborazzi record task (default
16
+ * `recordRoborazziDebug`) once. A build/compile failure means the project
17
+ * does not render and every screen is marked RENDER_UNSUPPORTED — the
18
+ * Android analog of the iOS probe gate (one Gradle module compiles as a
19
+ * unit, so a wrapper compile error cannot be isolated to one screen).
20
+ * 4. Locates each screen's Roborazzi golden inside the project (recorded as
21
+ * `build/outputs/roborazzi/...render<ScreenName>.png` from a test method
22
+ * named render<ScreenName>, or a flat `<ScreenName>.png`), runs it through
23
+ * the shared render-output
24
+ * post-processor (content trim, reference alignment, dimension
25
+ * reporting), and writes, inside the artifacts dir
26
+ * (${CIBUILD_ARTIFACTS_DIR:-.ci/artifacts}):
27
+ * - ui-fidelity/rendered/<Screen>.png
28
+ * - ui-fidelity/references/<Screen>.png
29
+ * - ui-fidelity/aligned/<Screen>.png
30
+ * - protocol-result.json (always written, even on partial/total failure)
31
+ * 5. Exits non-zero iff the archive was invalid or any screen is not
32
+ * "rendered".
33
+ *
34
+ * The result payload and artifact layout are identical in shape to the iOS
35
+ * ui-fidelity-render step, so the dashboard compare view, the artifact
36
+ * download tool, and the agent's compare flow are reused unchanged. Image
37
+ * paths in protocol-result.json are relative to the artifacts dir, and error
38
+ * messages are sanitized so absolute runner paths are rewritten before they
39
+ * are stored.
40
+ */
41
+ import { BaseStepExecutor } from './base.js';
42
+ import type { StepDef, CIConfig } from '../../types.js';
43
+ import type { ValidationRequirement } from '../validation-types.js';
44
+ /** Inputs for the ui-fidelity-render-android step. */
45
+ export interface UiFidelityRenderAndroidInputs {
46
+ /**
47
+ * Display scale factor applied when reporting logical points (default 2).
48
+ * Must match the density qualifier the render test pins (xhdpi = 2x), so
49
+ * logical_pt = rendered_px / scale lines up with design references.
50
+ */
51
+ scale?: number | string;
52
+ /**
53
+ * Gradle task that records the Roborazzi PNGs (default
54
+ * "recordRoborazziDebug"). Run from the project root, so it executes in
55
+ * whichever module declares it.
56
+ */
57
+ gradle_task?: string;
58
+ }
59
+ /**
60
+ * Default Roborazzi record task. Run from the project root so Gradle resolves
61
+ * it in whichever subproject declares it.
62
+ */
63
+ export declare const DEFAULT_GRADLE_TASK = "recordRoborazziDebug";
64
+ /** Basename of the shipped Gradle-project archive inside the run-inputs dir. */
65
+ export declare const PACKAGE_ARCHIVE_BASENAME = "package.tar.gz";
66
+ /** Renderer identifier recorded in protocol-result.json. */
67
+ export declare const ANDROID_RENDERER = "roborazzi-robolectric";
68
+ /**
69
+ * Validates a Gradle task name: a non-empty token of letters, digits, and the
70
+ * separators Gradle allows in a task path (`:`, `-`, `_`). Rejects shell
71
+ * metacharacters so the task is safe to pass to the runner.
72
+ */
73
+ export declare function isValidGradleTask(task: string): boolean;
74
+ /** Config baked into the generated runtime script at YAML-conversion time. */
75
+ export interface AndroidRenderScriptConfig {
76
+ scale: number;
77
+ gradleTask: string;
78
+ }
79
+ /**
80
+ * Generates the self-contained runtime node script for the step. Pure function
81
+ * of its config — exported so tests can execute the script directly.
82
+ */
83
+ export declare function generateAndroidRenderScript(config: AndroidRenderScriptConfig): string;
84
+ /**
85
+ * ui-fidelity-render-android step executor. Returns a node-kind StepDef whose
86
+ * script performs the render at runtime on the macOS runner.
87
+ */
88
+ export declare class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
89
+ getValidationRequirements(_inputs: UiFidelityRenderAndroidInputs, _env: Record<string, string>, _config: CIConfig): ValidationRequirement[];
90
+ execute(inputs: UiFidelityRenderAndroidInputs, _env: Record<string, string>, _config: CIConfig): Promise<StepDef>;
91
+ }
92
+ //# sourceMappingURL=ui-fidelity-render-android.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-fidelity-render-android.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render-android.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAIpE,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAEzD,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,8EAA8E;AAC9E,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAqnBD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM,CAUrF;AAED;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,gBAAgB;IACvE,yBAAyB,CACvB,OAAO,EAAE,6BAA6B,EACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,qBAAqB,EAAE;IAepB,OAAO,CACX,MAAM,EAAE,6BAA6B,EACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,OAAO,CAAC;CAgBpB"}