@invarn/cibuild 2.0.2 → 2.0.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ui-fidelity-render.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui-fidelity-render.d.ts","sourceRoot":"","sources":["../../../../src/yaml/steps/ui-fidelity-render.ts"],"names":[],"mappings":"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"}
@@ -865,6 +865,97 @@ function cleanupShippedPackage() {
865
865
  shippedExtractDir = null;
866
866
  }
867
867
  `;
868
+ /**
869
+ * Runtime support for asset catalogs (package_source variants only).
870
+ *
871
+ * `swift build` copies a target's .xcassets into its resource bundle but never
872
+ * runs actool, so SwiftUI's Image("name") finds no compiled catalog and the
873
+ * image does not resolve. After the harness builds, compile every .xcassets in
874
+ * the package with actool into the harness bin directory (= the render
875
+ * executable's Bundle.main), so unmodified Image("name") references resolve.
876
+ * One actool invocation over all catalogs (it merges them). Best-effort:
877
+ * a compile failure is logged and rendering continues -- the affected images
878
+ * simply will not appear, which the human comparing the screens will see.
879
+ */
880
+ const RENDER_SCRIPT_ASSET_CATALOG_STAGE = String.raw `
881
+ // ---- asset-catalog stage (package_source variants) ----
882
+
883
+ var ASSET_CATALOG_MIN_MACOS = '13.0';
884
+
885
+ // Recursively collect *.xcassets directories under root, skipping .build and
886
+ // the catalogs' own contents (a catalog is a leaf for this scan).
887
+ function findAssetCatalogs(root) {
888
+ var found = [];
889
+ var stack = [root];
890
+ while (stack.length > 0) {
891
+ var dir = stack.pop();
892
+ var entries;
893
+ try {
894
+ entries = fs.readdirSync(dir, { withFileTypes: true });
895
+ } catch (readError) {
896
+ continue;
897
+ }
898
+ for (var i = 0; i < entries.length; i++) {
899
+ var entry = entries[i];
900
+ if (!entry.isDirectory()) continue;
901
+ if (entry.name === '.build') continue;
902
+ var full = path.join(dir, entry.name);
903
+ if (entry.name.slice(-9) === '.xcassets') {
904
+ found.push(full);
905
+ } else {
906
+ stack.push(full);
907
+ }
908
+ }
909
+ }
910
+ return found.sort();
911
+ }
912
+
913
+ function compileAssetCatalogs(packageDir, harnessDir) {
914
+ var catalogs = findAssetCatalogs(packageDir);
915
+ if (catalogs.length === 0) return;
916
+
917
+ var binResult = runSwift(['build', '--package-path', harnessDir, '--show-bin-path']);
918
+ if (binResult.status !== 0) {
919
+ logError('asset-catalog compile skipped: could not resolve the build bin path');
920
+ return;
921
+ }
922
+ var binPath = String(binResult.stdout || '').trim().split('\n').pop().trim();
923
+ if (!binPath) {
924
+ logError('asset-catalog compile skipped: empty build bin path');
925
+ return;
926
+ }
927
+ try {
928
+ fs.mkdirSync(binPath, { recursive: true });
929
+ } catch (mkdirError) {
930
+ // best effort -- actool reports a missing output dir itself
931
+ }
932
+
933
+ var args = ['actool'].concat(catalogs);
934
+ args.push(
935
+ '--compile', binPath,
936
+ '--platform', 'macosx',
937
+ '--minimum-deployment-target', ASSET_CATALOG_MIN_MACOS,
938
+ '--output-format', 'human-readable-text'
939
+ );
940
+ log('xcrun ' + args.join(' '));
941
+ var result = cp.spawnSync('xcrun', args, {
942
+ encoding: 'utf-8',
943
+ maxBuffer: 16 * 1024 * 1024,
944
+ });
945
+ if (result.stdout) process.stdout.write(result.stdout);
946
+ if (result.stderr) process.stderr.write(result.stderr);
947
+ if (result.error || result.status !== 0) {
948
+ var detail = (result.stderr || '') +
949
+ (result.error ? String(result.error.message || result.error) : '');
950
+ logError(
951
+ 'asset-catalog compile failed (continuing; catalog images may not ' +
952
+ 'resolve): ' + sanitizeMessage(detail.trim())
953
+ );
954
+ return;
955
+ }
956
+ log('compiled ' + catalogs.length + ' asset catalog(s) for macOS');
957
+ }
958
+ `;
868
959
  /**
869
960
  * Replaces exactly one occurrence of `anchor` in `source`. Throws when the
870
961
  * anchor is missing or ambiguous, so any drift between the v1 runtime text
@@ -966,6 +1057,12 @@ function buildPackageSourceMain() {
966
1057
  ' }\n');
967
1058
  // The shipped-package stage is defined ahead of main().
968
1059
  main = replaceOnce(main, 'function main() {', RENDER_SCRIPT_PACKAGE_STAGE + '\nfunction main() {');
1060
+ // Asset-catalog stage: define the helpers ahead of renderWithHarness, then
1061
+ // compile any catalogs once the package has proven it builds (after the
1062
+ // probe) and before the per-screen renders that depend on the images.
1063
+ main = replaceOnce(main, 'function renderWithHarness(renderable, packagePath) {', RENDER_SCRIPT_ASSET_CATALOG_STAGE + '\nfunction renderWithHarness(renderable, packagePath) {');
1064
+ main = replaceOnce(main, ' renderable.forEach(function (entry) {', ' compileAssetCatalogs(packagePath, harnessDir);\n\n' +
1065
+ ' renderable.forEach(function (entry) {');
969
1066
  return main;
970
1067
  }
971
1068
  const RENDER_SCRIPT_MAIN_WITH_PACKAGE_SOURCE = buildPackageSourceMain();
@@ -58,15 +58,17 @@ const FAKE_SWIFT_LINES = [
58
58
  ' printf \'{"name":"shipped-package","products":[%s]}\\n\' "$json"',
59
59
  ' exit 0 ;;',
60
60
  ' build)',
61
- ' product=""; target=""; pkg=""',
61
+ ' product=""; target=""; pkg=""; showbin=""',
62
62
  ' while [ $# -gt 0 ]; do',
63
63
  ' case "$1" in',
64
64
  ' --product) product="$2"; shift 2 ;;',
65
65
  ' --target) target="$2"; shift 2 ;;',
66
66
  ' --package-path) pkg="$2"; shift 2 ;;',
67
+ ' --show-bin-path) showbin=1; shift ;;',
67
68
  ' *) shift ;;',
68
69
  ' esac',
69
70
  ' done',
71
+ ' if [ -n "$showbin" ]; then echo "$pkg/.build/debug"; exit 0; fi',
70
72
  ' name="${product:-$target}"',
71
73
  ' if [ "$name" = "RenderProbe" ]; then',
72
74
  ' if [ -n "$FAKE_SWIFT_PROBE_FAIL" ]; then',
@@ -109,6 +111,35 @@ const FAKE_SWIFT_LINES = [
109
111
  ' exit 0 ;;',
110
112
  'esac',
111
113
  ];
114
+ /**
115
+ * PATH-shimmed fake `xcrun` covering `xcrun actool`. Logs its args to
116
+ * $FAKE_XCRUN_LOG, writes a stub Assets.car into the --compile directory,
117
+ * and fails (exit 1) when FAKE_ACTOOL_FAIL is set so the warn-and-continue
118
+ * path is exercisable.
119
+ */
120
+ const FAKE_XCRUN_LINES = [
121
+ '#!/bin/bash',
122
+ '[ -n "$FAKE_XCRUN_LOG" ] && echo "$*" >> "$FAKE_XCRUN_LOG"',
123
+ 'sub="$1"; shift',
124
+ 'case "$sub" in',
125
+ ' actool)',
126
+ ' out=""',
127
+ ' while [ $# -gt 0 ]; do',
128
+ ' case "$1" in',
129
+ ' --compile) out="$2"; shift 2 ;;',
130
+ ' *) shift ;;',
131
+ ' esac',
132
+ ' done',
133
+ ' if [ -n "$FAKE_ACTOOL_FAIL" ]; then',
134
+ ' echo "error: actool: malformed asset catalog" >&2',
135
+ ' exit 1',
136
+ ' fi',
137
+ ' [ -n "$out" ] && mkdir -p "$out" && printf \'fake-car\' > "$out/Assets.car"',
138
+ ' exit 0 ;;',
139
+ ' *)',
140
+ ' exit 0 ;;',
141
+ 'esac',
142
+ ];
112
143
  const tempDirs = [];
113
144
  afterAll(() => {
114
145
  for (const dir of tempDirs) {
@@ -131,6 +162,9 @@ function makeProject(params) {
131
162
  const swiftPath = join(binDir, 'swift');
132
163
  writeFileSync(swiftPath, FAKE_SWIFT_LINES.join('\n') + '\n');
133
164
  chmodSync(swiftPath, 0o755);
165
+ const xcrunPath = join(binDir, 'xcrun');
166
+ writeFileSync(xcrunPath, FAKE_XCRUN_LINES.join('\n') + '\n');
167
+ chmodSync(xcrunPath, 0o755);
134
168
  if (params !== undefined) {
135
169
  writeFileSync(join(dir, '.ci', 'inputs', 'params.json'), typeof params === 'string' ? params : JSON.stringify(params));
136
170
  }
@@ -862,6 +896,47 @@ describe('render script runtime (package_source: repo)', () => {
862
896
  ],
863
897
  });
864
898
  });
899
+ test('compiles asset catalogs found in the package before rendering', async () => {
900
+ const project = makeProject({ screens: { HomeView: 'home.png' } });
901
+ writeReference(project, 'home.png');
902
+ // A catalog at the package root: SwiftPM ignores it, so the renderer must
903
+ // compile it with actool for Image("name") to resolve at render time.
904
+ const catalog = join(project.packageDir, 'Assets', 'Media.xcassets');
905
+ mkdirSync(catalog, { recursive: true });
906
+ writeFileSync(join(catalog, 'Contents.json'), '{}');
907
+ const script = await buildScript(project, { package_source: 'repo' });
908
+ const xcrunLog = join(project.dir, 'xcrun-invocations.log');
909
+ const run = runScript(project, script, { FAKE_XCRUN_LOG: xcrunLog });
910
+ expect(run.status).toBe(0);
911
+ expect(isPng(artifact(project, 'ui-fidelity/rendered/HomeView.png'))).toBe(true);
912
+ const xcrunCalls = readFileSync(xcrunLog, 'utf-8');
913
+ expect(xcrunCalls).toContain('actool');
914
+ expect(xcrunCalls).toContain('Media.xcassets');
915
+ expect(xcrunCalls).toContain('--compile');
916
+ expect(xcrunCalls).toContain('--platform macosx');
917
+ });
918
+ test('does not invoke actool when the package has no asset catalogs', async () => {
919
+ const project = makeProject({ screens: { HomeView: 'home.png' } });
920
+ writeReference(project, 'home.png');
921
+ const script = await buildScript(project, { package_source: 'repo' });
922
+ const xcrunLog = join(project.dir, 'xcrun-invocations.log');
923
+ const run = runScript(project, script, { FAKE_XCRUN_LOG: xcrunLog });
924
+ expect(run.status).toBe(0);
925
+ expect(isPng(artifact(project, 'ui-fidelity/rendered/HomeView.png'))).toBe(true);
926
+ expect(existsSync(xcrunLog)).toBe(false);
927
+ });
928
+ test('a failing asset-catalog compile warns but still renders (warn-and-continue)', async () => {
929
+ const project = makeProject({ screens: { HomeView: 'home.png' } });
930
+ writeReference(project, 'home.png');
931
+ const catalog = join(project.packageDir, 'Media.xcassets');
932
+ mkdirSync(catalog, { recursive: true });
933
+ writeFileSync(join(catalog, 'Contents.json'), '{}');
934
+ const script = await buildScript(project, { package_source: 'repo' });
935
+ const run = runScript(project, script, { FAKE_ACTOOL_FAIL: '1' });
936
+ expect(run.status).toBe(0);
937
+ expect(isPng(artifact(project, 'ui-fidelity/rendered/HomeView.png'))).toBe(true);
938
+ expect(run.stderr).toContain('asset-catalog compile failed');
939
+ });
865
940
  });
866
941
  describe('render script runtime (package_source: inputs)', () => {
867
942
  test('renders every screen from a shipped archive with a single package root', async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invarn/cibuild",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "CI Build CLI — local pipeline orchestration and validation",
5
5
  "type": "module",
6
6
  "main": "dist/cli.cjs",