@omega.js/desktop 0.50.0 → 0.51.0

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 (267) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/css/tokens/_index.scss +1 -1
  3. package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +4 -1
  4. package/dist/assets/themes/base/_includes/frontend/sections/footer.html +13 -6
  5. package/dist/assets/themes/base/_includes/frontend/sections/nav.html +10 -8
  6. package/dist/assets/themes/base/_includes/global/sections/account.html +3 -1
  7. package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +12 -8
  8. package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +10 -6
  9. package/dist/assets/themes/base/_includes/global/sections/page-header.html +8 -4
  10. package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +24 -24
  11. package/dist/assets/themes/base/_layouts/frontend/pages/about.html +10 -10
  12. package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +35 -35
  13. package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +4 -4
  14. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +1 -1
  15. package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +4 -4
  16. package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +3 -3
  17. package/dist/assets/themes/base/_layouts/frontend/pages/download.html +34 -33
  18. package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +11 -11
  19. package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +1 -1
  20. package/dist/assets/themes/base/_layouts/frontend/pages/status.html +1 -1
  21. package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +9 -7
  22. package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +5 -3
  23. package/dist/assets/themes/base/_sections/about/letter/section.html +1 -1
  24. package/dist/assets/themes/base/_sections/about/letter/section.json5 +4 -4
  25. package/dist/assets/themes/base/_sections/marketing/bento/section.html +1 -1
  26. package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +15 -15
  27. package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +1 -1
  28. package/dist/assets/themes/base/_sections/marketing/hero/section.html +6 -6
  29. package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +10 -10
  30. package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +1 -1
  31. package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +2 -2
  32. package/dist/assets/themes/base/_sections/marketing/stats/section.html +1 -1
  33. package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +5 -5
  34. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +1 -1
  35. package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +2 -2
  36. package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +10 -10
  37. package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +10 -10
  38. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +2 -2
  39. package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +4 -4
  40. package/dist/build.js +69 -28
  41. package/dist/cli-run.js +20 -13
  42. package/dist/cli.js +12 -7
  43. package/dist/commands/build.js +0 -2
  44. package/dist/commands/deploy.js +116 -44
  45. package/dist/commands/finalize-release.js +5 -4
  46. package/dist/commands/launch.js +12 -7
  47. package/dist/commands/lib/deploy-precheck.js +66 -34
  48. package/dist/commands/lib/ensure-target.js +71 -11
  49. package/dist/commands/package.js +0 -1
  50. package/dist/commands/publish.js +11 -4
  51. package/dist/commands/release.js +99 -252
  52. package/dist/commands/runner.js +42 -5
  53. package/dist/commands/sign-windows.js +46 -15
  54. package/dist/commands/test.js +4 -3
  55. package/dist/commands/validate-certs.js +291 -115
  56. package/dist/config/page-template.html +4 -0
  57. package/dist/defaults/.github/workflows/build.yml +144 -64
  58. package/dist/defaults/AGENTS.md +3 -2
  59. package/dist/defaults/_.gitignore +4 -2
  60. package/dist/defaults/config/certs/README.md +25 -45
  61. package/dist/defaults/config/omega.json5 +66 -32
  62. package/dist/defaults/hooks/deploy/pre.js +10 -0
  63. package/dist/defaults/src/assets/scss/main.scss +12 -2
  64. package/dist/defaults/src/integrations/tray/index.js +1 -1
  65. package/dist/gulp/main.js +13 -25
  66. package/dist/gulp/tasks/audit.js +18 -6
  67. package/dist/gulp/tasks/build-config.js +181 -60
  68. package/dist/gulp/tasks/bundle.js +108 -36
  69. package/dist/gulp/tasks/release.js +86 -4
  70. package/dist/gulp/tasks/sass.js +11 -0
  71. package/dist/hooks/lib/notarize-tools.js +137 -0
  72. package/dist/hooks/notarize-artifacts.js +57 -0
  73. package/dist/hooks/notarize.js +59 -8
  74. package/dist/lib/auth-persistence.js +25 -0
  75. package/dist/lib/client-bridge.js +11 -8
  76. package/dist/lib/deep-link.js +15 -8
  77. package/dist/lib/protocol.js +7 -1
  78. package/dist/lib/restart-manager/install.js +6 -3
  79. package/dist/lib/sign-helpers/auto-unlock.js +65 -27
  80. package/dist/lib/sign-helpers/console-lock.js +34 -0
  81. package/dist/lib/sign-helpers/exec-with-limit.js +68 -0
  82. package/dist/lib/sign-helpers/resolve-icons.js +6 -4
  83. package/dist/lib/tray.js +7 -6
  84. package/dist/main.js +16 -0
  85. package/dist/preload.js +8 -0
  86. package/dist/renderer.js +16 -4
  87. package/dist/runner/job-started.js +104 -0
  88. package/dist/test/fixtures/consumer-app/config/omega.json5 +7 -0
  89. package/dist/test/fixtures/consumer-app/package.json +1 -1
  90. package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +7 -1
  91. package/dist/test/harness/main-entry.js +2 -1
  92. package/dist/test/harness/renderer-preload.js +12 -3
  93. package/dist/test/runners/boot.js +101 -11
  94. package/dist/test/runners/electron.js +1 -1
  95. package/dist/test/suites/boot/consumer-app-boots.test.js +54 -0
  96. package/dist/test/suites/build/audit.test.js +45 -6
  97. package/dist/test/suites/build/auth-persistence-resolve.test.js +119 -0
  98. package/dist/test/suites/build/auto-unlock.test.js +105 -0
  99. package/dist/test/suites/build/boot-runner-timeout.test.js +253 -0
  100. package/dist/test/suites/build/brand-scss.test.js +106 -0
  101. package/dist/test/suites/build/build-config.test.js +146 -36
  102. package/dist/test/suites/build/build-json-bake.test.js +245 -0
  103. package/dist/test/suites/build/build-verbs.test.js +48 -15
  104. package/dist/test/suites/build/build-workflow-jobs.test.js +190 -0
  105. package/dist/test/suites/build/cli.test.js +32 -10
  106. package/dist/test/suites/build/config-schema.test.js +4 -4
  107. package/dist/test/suites/build/console-lock.test.js +51 -0
  108. package/dist/test/suites/build/defaults-scaffold.test.js +71 -2
  109. package/dist/test/suites/build/deploy-direct.test.js +241 -0
  110. package/dist/test/suites/build/deploy-dispatch.test.js +287 -0
  111. package/dist/test/suites/build/deploy-hook.test.js +169 -0
  112. package/dist/test/suites/build/ensure-target.test.js +14 -2
  113. package/dist/test/suites/build/env-delivery.test.js +19 -10
  114. package/dist/test/suites/build/env-watch.test.js +18 -7
  115. package/dist/test/suites/build/esm-only-dependency.test.js +127 -0
  116. package/dist/test/suites/build/exec-with-limit.test.js +53 -0
  117. package/dist/test/suites/build/finalize-release.test.js +2 -2
  118. package/dist/test/suites/build/get-config.test.js +120 -8
  119. package/dist/test/suites/build/github-utils.test.js +12 -6
  120. package/dist/test/suites/build/license-stamp.test.js +6 -4
  121. package/dist/test/suites/build/manager.test.js +88 -68
  122. package/dist/test/suites/build/manifest-deps.test.js +116 -0
  123. package/dist/test/suites/build/merge-line-files.test.js +27 -4
  124. package/dist/test/suites/build/notarize-artifacts.test.js +135 -0
  125. package/dist/test/suites/build/notarize-tools.test.js +38 -0
  126. package/dist/test/suites/build/notarize.test.js +207 -0
  127. package/dist/test/suites/build/release-pipeline.test.js +38 -60
  128. package/dist/test/suites/build/release-skipped-upload.test.js +107 -0
  129. package/dist/test/suites/build/resolve-icons.test.js +35 -35
  130. package/dist/test/suites/build/runner-job-guard.test.js +182 -0
  131. package/dist/test/suites/build/runner.test.js +25 -2
  132. package/dist/test/suites/build/sentry.test.js +8 -3
  133. package/dist/test/suites/build/setup-scripts.test.js +3 -0
  134. package/dist/test/suites/build/sign-windows.test.js +92 -8
  135. package/dist/test/suites/build/test-stealth.test.js +17 -11
  136. package/dist/test/suites/build/url-helpers.test.js +37 -17
  137. package/dist/test/suites/build/validate-certs.test.js +428 -55
  138. package/dist/test/suites/build/validate-config.test.js +3 -3
  139. package/dist/test/suites/main/auth-flow.test.js +12 -0
  140. package/dist/test/suites/main/auth-persistence.test.js +14 -17
  141. package/dist/test/suites/main/auto-updater.test.js +2 -2
  142. package/dist/test/suites/main/boot-sequence.test.js +1 -1
  143. package/dist/test/suites/main/client-bridge.integration.test.js +5 -82
  144. package/dist/test/suites/main/client-bridge.test.js +19 -6
  145. package/dist/test/suites/main/deep-link.test.js +54 -0
  146. package/dist/test/suites/main/startup-paths-and-ua.test.js +1 -1
  147. package/dist/test/suites/main/url-helpers.test.js +81 -72
  148. package/dist/test/suites/renderer/cross-context-helpers.test.js +19 -16
  149. package/dist/utils/build-pipeline.js +10 -10
  150. package/dist/utils/github.js +12 -51
  151. package/dist/utils/load-env.js +66 -0
  152. package/dist/utils/mode-helpers.js +43 -111
  153. package/dist/utils/platform.js +37 -0
  154. package/dist/utils/runner-env.js +2 -1
  155. package/dist/utils/runner-job-guard.js +149 -0
  156. package/dist/utils/ship-keys.js +52 -0
  157. package/dist/utils/test-stealth.js +5 -3
  158. package/dist/utils/url-helpers.js +33 -17
  159. package/dist/vendor/config/bundle-id.js +53 -0
  160. package/dist/vendor/config/client-config.js +141 -0
  161. package/dist/vendor/config/company.js +334 -15
  162. package/dist/vendor/config/dev-facts.js +48 -0
  163. package/dist/vendor/config/env-delivery.js +231 -9
  164. package/dist/vendor/config/env-retired.js +137 -0
  165. package/dist/vendor/config/env-rules.js +22 -3
  166. package/dist/vendor/config/env-schema.js +234 -117
  167. package/dist/vendor/config/env.js +55 -26
  168. package/dist/vendor/config/environment.js +189 -0
  169. package/dist/vendor/config/hooks.js +13 -11
  170. package/dist/vendor/config/index.js +121 -44
  171. package/dist/vendor/config/load.js +366 -115
  172. package/dist/vendor/config/merge.js +2 -2
  173. package/dist/vendor/config/order.js +3 -3
  174. package/dist/vendor/config/platforms.js +276 -0
  175. package/dist/vendor/config/repo.js +226 -104
  176. package/dist/vendor/config/retired-keys.js +232 -27
  177. package/dist/vendor/config/schema.js +525 -99
  178. package/dist/vendor/config/site-global.js +63 -53
  179. package/dist/vendor/config/targets.js +187 -0
  180. package/dist/vendor/config/validate.js +119 -59
  181. package/dist/vendor/devkit/argv.js +118 -0
  182. package/dist/vendor/devkit/attach-log-file.js +21 -13
  183. package/dist/vendor/devkit/brand-tokens.js +278 -0
  184. package/dist/vendor/devkit/brand-version.js +264 -0
  185. package/dist/vendor/devkit/build-json.js +91 -0
  186. package/dist/vendor/devkit/bundle.js +48 -0
  187. package/dist/vendor/devkit/certificate-expiry.js +108 -0
  188. package/dist/vendor/devkit/certs.js +16 -194
  189. package/dist/vendor/devkit/ci-workflows.js +124 -8
  190. package/dist/vendor/devkit/cli-router.js +3 -3
  191. package/dist/vendor/devkit/defaults-engine.js +69 -7
  192. package/dist/vendor/devkit/deploy-follow.js +297 -0
  193. package/dist/vendor/devkit/deploy-precheck.js +23 -8
  194. package/dist/vendor/devkit/deploy-record.js +11 -27
  195. package/dist/vendor/devkit/deploy-snapshot.js +661 -0
  196. package/dist/vendor/devkit/deploy.js +445 -75
  197. package/dist/vendor/devkit/git-auth.js +73 -0
  198. package/dist/vendor/devkit/git-remote.js +95 -0
  199. package/dist/vendor/devkit/github-repo.js +290 -0
  200. package/dist/vendor/devkit/local.js +47 -0
  201. package/dist/vendor/devkit/merge-line-files.js +23 -16
  202. package/dist/vendor/devkit/omega-bin.js +18 -3
  203. package/dist/vendor/devkit/pack-local.js +391 -0
  204. package/dist/vendor/devkit/preludes/index.js +120 -0
  205. package/dist/vendor/devkit/preludes/origin-heal.js +156 -0
  206. package/dist/vendor/devkit/service-account.js +43 -0
  207. package/dist/vendor/devkit/ship-plan.js +112 -0
  208. package/dist/vendor/devkit/signing-env.js +180 -0
  209. package/dist/vendor/devkit/signing-tree.js +92 -0
  210. package/dist/vendor/devkit/target-seams.js +142 -0
  211. package/dist/vendor/devkit/target-secrets.js +235 -50
  212. package/dist/vendor/devkit/test/esm-only-fixture.js +48 -0
  213. package/dist/vendor/devkit/test/fixtures/esm-only-package/browser.js +19 -0
  214. package/dist/vendor/devkit/test/fixtures/esm-only-package/index.js +20 -0
  215. package/dist/vendor/devkit/test/fixtures/esm-only-package/package.json +13 -0
  216. package/dist/vendor/monitoring/env.js +20 -10
  217. package/dist/vendor/monitoring/main.js +1 -1
  218. package/dist/vendor/monitoring/preload.js +1 -1
  219. package/dist/vendor/monitoring/renderer.js +1 -1
  220. package/docs/analytics.md +1 -1
  221. package/docs/auto-updater.md +5 -5
  222. package/docs/boot-sequence.md +1 -1
  223. package/docs/build-system.md +15 -7
  224. package/docs/client-bridge.md +9 -7
  225. package/docs/config-schema.md +4 -4
  226. package/docs/css.md +8 -2
  227. package/docs/deep-link.md +12 -4
  228. package/docs/environment-detection.md +32 -24
  229. package/docs/hooks.md +3 -1
  230. package/docs/icons.md +7 -7
  231. package/docs/index.md +61 -23
  232. package/docs/installer-options.md +24 -21
  233. package/docs/logging.md +5 -5
  234. package/docs/releasing.md +25 -17
  235. package/docs/runner.md +40 -5
  236. package/docs/shared/brands.md +12 -6
  237. package/docs/shared/breaking-changes.md +375 -21
  238. package/docs/shared/config.md +757 -199
  239. package/docs/shared/deploys.md +194 -91
  240. package/docs/shared/icons.md +18 -0
  241. package/docs/shared/local-dev.md +24 -6
  242. package/docs/shared/logging.md +9 -6
  243. package/docs/shared/monitoring.md +27 -13
  244. package/docs/shared/publishing.md +3 -3
  245. package/docs/shared/rulings.md +2 -2
  246. package/docs/shared/testing.md +1 -1
  247. package/docs/shared/theming.md +26 -1
  248. package/docs/shared/translation.md +49 -7
  249. package/docs/shared/updates.md +1 -1
  250. package/docs/signing.md +59 -33
  251. package/docs/test-framework.md +10 -5
  252. package/docs/themes.md +15 -1
  253. package/package.json +18 -12
  254. package/bin/omega-desktop +0 -2
  255. package/dist/commands/push-secrets.js +0 -141
  256. package/dist/test/suites/build/deliver-certs.test.js +0 -95
  257. package/dist/test/suites/build/derive-signing-env.test.js +0 -122
  258. package/dist/test/suites/build/push-secrets.test.js +0 -226
  259. package/dist/test/suites/build/resolve-signing-cert.test.js +0 -342
  260. package/dist/utils/deliver-certs.js +0 -69
  261. package/dist/utils/derive-signing-env.js +0 -56
  262. package/dist/utils/resolve-signing-cert.js +0 -175
  263. package/dist/vendor/config/desktop-artifacts.js +0 -110
  264. package/dist/vendor/config/instances.js +0 -208
  265. /package/dist/defaults/config/icons/{macos → mac}/dmg.png +0 -0
  266. /package/dist/defaults/config/icons/{macos → mac}/icon.png +0 -0
  267. /package/dist/defaults/config/icons/{macos → mac}/tray.png +0 -0
@@ -0,0 +1,169 @@
1
+ // Build-layer test for the deploy verb's consumer hook
2
+ // ([#899](https://github.com/Omega-JS-Stack/omega/issues/899)).
3
+ //
4
+ // `omega deploy` runs the consumer's `hooks/deploy/pre.js` after the local
5
+ // scaffold and BEFORE the network precheck, on both lanes, through the same
6
+ // runner the build and release hooks use. A dry run skips it: a hook may act on
7
+ // the world (the playground's prunes its GitHub releases), and a dry run
8
+ // promises to send nothing.
9
+
10
+ const path = require('path');
11
+ const fs = require('fs');
12
+ const os = require('os');
13
+ const Module = require('module');
14
+ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
15
+ const attachLogFile = require('../../../vendor/devkit/attach-log-file.js');
16
+
17
+ const COMMANDS = path.join(__dirname, '..', '..', '..', 'commands');
18
+ const DEPLOY = path.join(COMMANDS, 'deploy.js');
19
+
20
+ /**
21
+ * Swap ONE module for a recorder and hand back the undo (the deploy-direct
22
+ * test's seam: the scaffold and the precheck are the two steps around the
23
+ * hook, and everything else in the verb is the real code).
24
+ *
25
+ * @param {string} file - The module to replace.
26
+ * @param {object} exports - What it exports for the duration.
27
+ * @returns {Function} The restore.
28
+ */
29
+ function stubModule(file, exports) {
30
+ const resolved = require.resolve(file);
31
+ const previous = require.cache[resolved];
32
+ const stub = new Module(resolved);
33
+
34
+ stub.filename = resolved;
35
+ stub.loaded = true;
36
+ stub.exports = exports;
37
+ require.cache[resolved] = stub;
38
+
39
+ return () => {
40
+ if (previous) require.cache[resolved] = previous;
41
+ else delete require.cache[resolved];
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Run the deploy verb against a consumer dir whose `hooks/deploy/pre.js`
47
+ * records its call, with the precheck stubbed to stop the run right there.
48
+ *
49
+ * @param {object} options - The verb's options.
50
+ * @returns {Promise<string[]>} The steps that ran, in order.
51
+ */
52
+ async function runDeploy(options) {
53
+ const ran = [];
54
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-deploy-hook-'));
55
+
56
+ fs.mkdirSync(path.join(tmp, 'hooks', 'deploy'), { recursive: true });
57
+ fs.writeFileSync(path.join(tmp, 'package.json'), '{"name":"desktop-deploy-hook-test"}');
58
+ fs.writeFileSync(
59
+ path.join(tmp, 'hooks', 'deploy', 'pre.js'),
60
+ `module.exports = async (ctx) => { global.__omegaDeployHookRan.push('deploy/pre:' + (ctx.projectRoot === process.cwd() && typeof ctx.manager === 'object' && ctx.mode === 'production' ? 'ctx ok' : 'ctx wrong')); };`,
61
+ );
62
+
63
+ const restores = [
64
+ stubModule(path.join(COMMANDS, 'lib', 'ensure-target.js'), { ensureTarget: async () => ran.push('ensure-target') }),
65
+ stubModule(path.join(COMMANDS, 'lib', 'deploy-precheck.js'), { deployPrecheck: async () => { ran.push('precheck'); throw new Error('STOP AT PRECHECK'); } }),
66
+ ];
67
+ const resolved = require.resolve(DEPLOY);
68
+ const cached = require.cache[resolved];
69
+ const origCwd = process.cwd();
70
+
71
+ delete require.cache[resolved];
72
+ global.__omegaDeployHookRan = ran;
73
+
74
+ try {
75
+ // Required BEFORE the chdir: the verb builds its Manager at load time from
76
+ // the real target; only the hook lookup reads cwd, at call time.
77
+ const deploy = require(resolved);
78
+ process.chdir(tmp);
79
+ await deploy(options).catch((e) => ran.push(e.message));
80
+ return ran;
81
+ } finally {
82
+ // The verb tees this process' writers now (#873): hand them back.
83
+ attachLogFile.detach();
84
+ process.chdir(origCwd);
85
+ delete global.__omegaDeployHookRan;
86
+ for (const restore of restores) restore();
87
+ if (cached) require.cache[resolved] = cached;
88
+ else delete require.cache[resolved];
89
+ fs.rmSync(tmp, { recursive: true, force: true });
90
+ }
91
+ }
92
+
93
+ /**
94
+ * The same seam, run inside a DRIFTED brand: the root says 0.0.3 and this
95
+ * target still says 0.0.2 ([#869](https://github.com/Omega-JS-Stack/omega/issues/869)).
96
+ *
97
+ * @returns {Promise<string[]>} The steps that ran, in order.
98
+ */
99
+ async function runDriftedDeploy() {
100
+ const ran = [];
101
+ const brand = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-deploy-drift-')));
102
+ const target = path.join(brand, 'targets', 'desktop');
103
+
104
+ fs.mkdirSync(path.join(brand, 'config'), { recursive: true });
105
+ fs.writeFileSync(path.join(brand, 'config', 'omega.json5'), '{ brand: { id: \'fixture\' }, targets: { desktop: { type: \'desktop\' } } }');
106
+ fs.writeFileSync(path.join(brand, 'package.json'), '{"name":"fixture-brand","version":"0.0.3"}');
107
+ fs.mkdirSync(target, { recursive: true });
108
+ fs.writeFileSync(path.join(target, 'package.json'), '{"name":"fixture-desktop","version":"0.0.2"}');
109
+
110
+ const restores = [
111
+ stubModule(path.join(COMMANDS, 'lib', 'ensure-target.js'), { ensureTarget: async () => ran.push('ensure-target') }),
112
+ stubModule(path.join(COMMANDS, 'lib', 'deploy-precheck.js'), { deployPrecheck: async () => { ran.push('precheck'); throw new Error('STOP AT PRECHECK'); } }),
113
+ ];
114
+ const resolved = require.resolve(DEPLOY);
115
+ const cached = require.cache[resolved];
116
+ const origCwd = process.cwd();
117
+
118
+ delete require.cache[resolved];
119
+
120
+ try {
121
+ const deploy = require(resolved);
122
+ process.chdir(target);
123
+ await deploy({}).catch((e) => ran.push(e.message));
124
+ return ran;
125
+ } finally {
126
+ // The verb tees this process' writers now (#873): hand them back.
127
+ attachLogFile.detach();
128
+ process.chdir(origCwd);
129
+ for (const restore of restores) restore();
130
+ if (cached) require.cache[resolved] = cached;
131
+ else delete require.cache[resolved];
132
+ fs.rmSync(brand, { recursive: true, force: true });
133
+ }
134
+ }
135
+
136
+ module.exports = defineCases({
137
+ type: 'suite',
138
+ layer: 'build',
139
+ description: 'deploy: the consumer\'s hooks/deploy/pre.js runs before the precheck (#899)',
140
+ tests: [
141
+ {
142
+ name: 'the hook runs after the scaffold and before the network precheck, with { manager, projectRoot, mode: production }',
143
+ run: async (ctx) => {
144
+ const ran = await runDeploy({});
145
+ ctx.expect(ran).toEqual(['ensure-target', 'deploy/pre:ctx ok', 'precheck', 'STOP AT PRECHECK']);
146
+ },
147
+ },
148
+ {
149
+ name: 'a dry run skips the hook: a hook may act on the world, and a dry run sends nothing',
150
+ run: async (ctx) => {
151
+ const ran = await runDeploy({ dryRun: true });
152
+ ctx.expect(ran).toEqual(['ensure-target', 'precheck', 'STOP AT PRECHECK']);
153
+ },
154
+ },
155
+ {
156
+ name: 'a target whose version drifted from the brand root is refused before the hook and the precheck (#869)',
157
+ run: async (ctx) => {
158
+ const ran = await runDriftedDeploy();
159
+
160
+ ctx.expect(ran.length).toBe(2);
161
+ ctx.expect(ran[0]).toBe('ensure-target');
162
+ ctx.expect(ran[1]).toContain('targets/desktop is 0.0.2 but the brand is 0.0.3');
163
+ ctx.expect(ran[1]).toContain('omega bump');
164
+ // The precheck never ran: a drifted target must not push its secrets.
165
+ ctx.expect(ran).not.toContain('precheck');
166
+ },
167
+ },
168
+ ],
169
+ });
@@ -22,8 +22,8 @@ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
22
22
  const package = Manager.getPackage('main');
23
23
 
24
24
  // A consumer whose peer deps are already satisfied — the steady state.
25
- function stageConsumer() {
26
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-ensure-'));
25
+ function stageConsumer(dir) {
26
+ const tmp = dir || fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-ensure-'));
27
27
  const devDependencies = { [package.name]: `^${package.version}` };
28
28
  for (const [name, ver] of Object.entries(package.peerDependencies || {})) {
29
29
  devDependencies[name] = ver;
@@ -117,6 +117,18 @@ module.exports = defineCases({
117
117
  ctx.expect(result.ran).toEqual(ran);
118
118
  },
119
119
  },
120
+ {
121
+ // Both SIGNING rungs stop the deploy instead of narrating it (#891): a
122
+ // refused or half secret publish hands CI a set it cannot sign with.
123
+ name: 'deploy precheck: validate-certs and push-secrets are FATAL (#891)',
124
+ run: (ctx) => {
125
+ const { STEPS } = require(path.join(__dirname, '..', '..', '..', 'commands', 'lib', 'deploy-precheck.js'));
126
+
127
+ ctx.expect(STEPS.map((step) => step.name)).toEqual(['framework-freshness', 'validate-certs', 'provision-repos', 'push-secrets']);
128
+ ctx.expect(STEPS.filter((step) => step.fatal === true).map((step) => step.name))
129
+ .toEqual(['validate-certs', 'push-secrets']);
130
+ },
131
+ },
120
132
  {
121
133
  name: 'cli: no `setup` command, and a bare invocation resolves to help',
122
134
  run: (ctx) => {
@@ -1,7 +1,8 @@
1
1
  // The env schema is the ONE declaration of how a key reaches the desktop app
2
2
  // ([#627](https://github.com/Omega-JS-Stack/omega/issues/627)): the build
3
- // workflow's env block, the bundle task's bake, and `omega push-secrets` all read
4
- // `delivery: { desktop: … }` and nothing else. Each used to carry its own
3
+ // workflow's env block, the bundle task's bake, and the deploy precheck's
4
+ // secrets publish all read `delivery: { desktop: … }` and nothing else. Each
5
+ // used to carry its own
5
6
  // hand-kept list — the workflow's was 21 lines a human maintained across four
6
7
  // job/step blocks, and a key added to one never reached the others.
7
8
  //
@@ -15,12 +16,13 @@ const os = require('os');
15
16
  const jetpack = require('fs-jetpack');
16
17
 
17
18
  const { publishSecretKeys, bakeKeys, renderSecretsBlock, WORKFLOW_OWNED_KEYS } = require('../../../vendor/config/env-delivery.js');
19
+ const { composeTargetEnv } = require('../../../vendor/config/index.js');
18
20
 
19
21
  const SRC = path.join(__dirname, '..', '..', '..');
20
22
  const Manager = require(path.join(SRC, 'build.js'));
21
23
  const { ensureTarget } = require(path.join(SRC, 'commands', 'lib', 'ensure-target.js'));
22
24
  const bundleTask = require(path.join(SRC, 'gulp', 'tasks', 'bundle.js'));
23
- const pushSecrets = require(path.join(SRC, 'commands', 'push-secrets.js'));
25
+ const { collectTargetSecrets } = require('../../../vendor/devkit/target-secrets.js');
24
26
  const defineCases = require('../../../vendor/devkit/test/define-cases.js');
25
27
 
26
28
  const package = Manager.getPackage('main');
@@ -51,7 +53,7 @@ function tmpBrand(brandEnv) {
51
53
  module.exports = defineCases({
52
54
  type: 'group',
53
55
  layer: 'build',
54
- description: 'env delivery (#627) — the workflow block, the bake list, and push-secrets from ONE schema',
56
+ description: 'env delivery (#627): the workflow block, the bake list, and the secrets publish from ONE schema',
55
57
  tests: [
56
58
  {
57
59
  name: 'the build workflow renders the schema block — every CI key, no hand list, no token left',
@@ -74,8 +76,10 @@ module.exports = defineCases({
74
76
  // The two DELIBERATE step-level overrides survive: the mac signing
75
77
  // assets are decoded to files, so those two names are paths there,
76
78
  // not the base64 secrets the workflow env carries.
77
- ctx.expect(workflow).toContain('CSC_LINK: config/certs/dev-id.p12');
78
- ctx.expect(workflow).toContain('APPLE_API_KEY: config/certs/AuthKey.p8');
79
+ // Each override is gated on its secret existing (#872): an empty secret
80
+ // reads as UNSET after the sanitize pass, so a brand with no cert builds unsigned.
81
+ ctx.expect(workflow).toContain("CSC_LINK: ${{ secrets.CSC_LINK != '' && 'config/certs/dev-id.p12' || '' }}");
82
+ ctx.expect(workflow).toContain("APPLE_API_KEY: ${{ secrets.APPLE_API_KEY != '' && 'config/certs/AuthKey.p8' || '' }}");
79
83
  } finally {
80
84
  fs.rmSync(tmp, { recursive: true, force: true });
81
85
  }
@@ -142,7 +146,7 @@ module.exports = defineCases({
142
146
  },
143
147
  },
144
148
  {
145
- name: 'push-secrets pushes the schema DELIVERY set — a key CI never reads stays home',
149
+ name: 'the secrets publish sends the schema DELIVERY set: a key CI never reads stays home',
146
150
  run: (ctx) => {
147
151
  const { brand, target } = tmpBrand([
148
152
  'GH_TOKEN=brand-token',
@@ -154,9 +158,14 @@ module.exports = defineCases({
154
158
  ].join('\n'));
155
159
 
156
160
  try {
157
- const keys = Object.keys(pushSecrets.collectEnvSecrets(target));
158
- ctx.expect(keys.sort()).toEqual(['APPLE_TEAM_ID', 'GH_TOKEN', 'GOOGLE_ANALYTICS_SECRET']);
159
- ctx.expect(keys.every((key) => publishSecretKeys('desktop').includes(key))).toBe(true);
161
+ const values = composeTargetEnv({ targetDir: target, target: 'desktop', environment: 'production' }).values;
162
+ const keys = Object.keys(collectTargetSecrets({ targetDir: target, target: 'desktop' }));
163
+
164
+ // OMEGA_ADMIN_KEY is the backend's and stays home; MY_CUSTOM_THING is
165
+ // a key the schema never declared, so it is the CONSUMER's own and
166
+ // travels to every target ([#835](https://github.com/Omega-JS-Stack/omega/issues/835)).
167
+ ctx.expect(keys.sort()).toEqual(['APPLE_TEAM_ID', 'GH_TOKEN', 'GOOGLE_ANALYTICS_SECRET', 'MY_CUSTOM_THING']);
168
+ ctx.expect(keys.every((key) => publishSecretKeys('desktop', { values }).includes(key))).toBe(true);
160
169
  } finally {
161
170
  fs.rmSync(brand, { recursive: true, force: true });
162
171
  }
@@ -20,20 +20,30 @@ const SRC = path.join(__dirname, '..', '..', '..');
20
20
  const serveTask = require(path.join(SRC, 'gulp', 'tasks', 'serve.js'));
21
21
  const defineCases = require('../../../vendor/devkit/test/define-cases.js');
22
22
 
23
- // A desktop target inside a brand monorepo, with a company root above it:
24
- // <root>/company/.env, <root>/brand/.env, <root>/brand/targets/desktop/.
23
+ // A desktop target inside a brand monorepo whose brand names a COMPANY (#677):
24
+ // the parent brand's company/ tree holds the shared .env, and the machine
25
+ // registry line (the parent's own runs write it) is what says where it is.
25
26
  function seedBrand() {
26
27
  const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-env-watch-')));
27
- const companyRoot = path.join(root, 'company');
28
+ const parentRoot = path.join(root, 'parent');
29
+ const companyRoot = path.join(parentRoot, 'company');
28
30
  const brandRoot = path.join(root, 'brand');
29
31
  const targetDir = path.join(brandRoot, 'targets', 'desktop');
30
32
 
33
+ jetpack.write(path.join(parentRoot, 'config', 'omega.json5'), '{ brand: { id: "fixture-co" }, company: { id: "self" } }');
31
34
  jetpack.write(path.join(companyRoot, '.env'), '');
32
- jetpack.write(path.join(brandRoot, '.omega', 'company.json'), JSON.stringify({ root: companyRoot }));
33
- jetpack.write(path.join(brandRoot, 'config', 'omega.json5'), '{ brand: { id: "fixture" }, targets: { desktop: {} } }');
35
+ jetpack.write(path.join(brandRoot, 'config', 'omega.json5'), '{ brand: { id: "fixture" }, company: { id: "fixture-co" }, targets: { desktop: { type: "desktop" } } }');
36
+ jetpack.write(path.join(root, 'home', 'brands.json'), JSON.stringify({ 'fixture-co': { root: parentRoot } }));
34
37
  jetpack.dir(targetDir);
35
38
 
36
- return { root, companyRoot, brandRoot, targetDir };
39
+ const previous = process.env.OMEGA_HOME;
40
+ process.env.OMEGA_HOME = path.join(root, 'home');
41
+ const restore = () => {
42
+ if (previous === undefined) delete process.env.OMEGA_HOME;
43
+ else process.env.OMEGA_HOME = previous;
44
+ };
45
+
46
+ return { root, companyRoot, brandRoot, targetDir, restore };
37
47
  }
38
48
 
39
49
  module.exports = defineCases({
@@ -44,7 +54,7 @@ module.exports = defineCases({
44
54
  {
45
55
  name: 'the serve watcher lists company, brand and target — overlays included',
46
56
  run: (ctx) => {
47
- const { root, companyRoot, brandRoot, targetDir } = seedBrand();
57
+ const { root, companyRoot, brandRoot, targetDir, restore } = seedBrand();
48
58
  const watcher = serveTask.watchEnvSources(targetDir, { environment: 'development' });
49
59
 
50
60
  try {
@@ -61,6 +71,7 @@ module.exports = defineCases({
61
71
  ]);
62
72
  } finally {
63
73
  watcher.close();
74
+ restore();
64
75
  fs.rmSync(root, { recursive: true, force: true });
65
76
  }
66
77
  },
@@ -0,0 +1,127 @@
1
+ // An ESM-only dependency, bundled into main and preload
2
+ // ([#906](https://github.com/Omega-JS-Stack/omega/issues/906)).
3
+ //
4
+ // Both Node bundles are CommonJS (`format: 'cjs'`, `platform: 'node'`), and
5
+ // esbuild rewrites `import.meta` to `{}` in that output: a dependency doing
6
+ // `createRequire(import.meta.url)` was handed `undefined` and threw
7
+ // ERR_INVALID_ARG_VALUE the moment a packaged app booted (found porting an
8
+ // electron-manager app that depends on yargs 18). @omega.js/devkit's `bundle()`
9
+ // composes the answer for every cjs+node build, so nothing here wires it.
10
+ //
11
+ // The proof is a RUN, not a read of the bundle text: the fixture package
12
+ // (@omega.js/devkit's `test/fixtures/esm-only-package`, the one every target
13
+ // proves this with) is installed into a staged project the way a brand installs
14
+ // a dependency, the REAL bundle task builds it, and the emitted bundle is
15
+ // executed under Node exactly as Electron executes `dist/main.bundle.js`.
16
+
17
+ const fs = require('fs');
18
+ const os = require('os');
19
+ const path = require('path');
20
+ const { spawnSync } = require('child_process');
21
+ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
22
+ const { installEsmOnlyFixture, FIXTURE_NAME, NODE_MARKER } = require('../../../vendor/devkit/test/esm-only-fixture.js');
23
+
24
+ const SRC = path.join(__dirname, '..', '..', '..');
25
+ const TASK = path.join(SRC, 'gulp', 'tasks', 'bundle.js');
26
+
27
+ // What each entry prints once it has loaded the ESM-only dependency: the run's
28
+ // whole answer, on one line, so the test reads it out of stdout.
29
+ function probe(name) {
30
+ return [
31
+ `const fixture = require(${JSON.stringify(FIXTURE_NAME)});`,
32
+ `console.log('${name}:' + JSON.stringify({ fileUrl: fixture.fileUrl, viaRequire: fixture.viaRequire, marker: fixture.marker }));`,
33
+ '',
34
+ ].join('\n');
35
+ }
36
+
37
+ // A desktop project with the fixture installed, built by the real task. The
38
+ // task reads its project (package.json / config / dist) from cwd at require
39
+ // time, so the module is flushed and required fresh inside the staged dir, the
40
+ // way every other build suite drives it.
41
+ async function buildStagedProject() {
42
+ const tmp = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-esm-only-')));
43
+ fs.writeFileSync(path.join(tmp, 'package.json'), '{ "name": "staged-desktop", "version": "1.0.0" }');
44
+
45
+ const write = (relative, contents) => {
46
+ const full = path.join(tmp, relative);
47
+ fs.mkdirSync(path.dirname(full), { recursive: true });
48
+ fs.writeFileSync(full, contents);
49
+ };
50
+ write('config/omega.json5', `{
51
+ brand: { id: 'staged', name: 'Staged' },
52
+ cloud: { config: { apiKey: 'AIza-staged', projectId: 'demo-staged' } },
53
+ targets: { desktop: { type: 'desktop' } },
54
+ }`);
55
+ write('src/main.js', probe('main'));
56
+ write('src/preload.js', probe('preload'));
57
+
58
+ // The dependency as npm installs one: a package directory under the
59
+ // consumer's own node_modules, found by the bundler's normal resolution.
60
+ installEsmOnlyFixture(tmp);
61
+
62
+ const oldCwd = process.cwd();
63
+ const flush = () => {
64
+ for (const key of Object.keys(require.cache)) {
65
+ if (key.startsWith(SRC + path.sep)) delete require.cache[key];
66
+ }
67
+ };
68
+
69
+ flush();
70
+ try {
71
+ process.chdir(tmp);
72
+ const task = require(TASK);
73
+ await new Promise((resolve, reject) => task((error) => (error ? reject(error) : resolve())));
74
+ } finally {
75
+ process.chdir(oldCwd);
76
+ flush();
77
+ }
78
+
79
+ return tmp;
80
+ }
81
+
82
+ // Run an emitted Node bundle the way Electron runs it: a plain Node process on
83
+ // the file the build wrote.
84
+ function run(file, name) {
85
+ const ran = spawnSync(process.execPath, [file], { encoding: 'utf8' });
86
+ const line = (ran.stdout || '').split('\n').find((text) => text.startsWith(`${name}:`));
87
+
88
+ return {
89
+ status: ran.status,
90
+ stderr: ran.stderr,
91
+ result: line ? JSON.parse(line.slice(name.length + 1)) : null,
92
+ };
93
+ }
94
+
95
+ module.exports = defineCases({
96
+ type: 'suite',
97
+ layer: 'build',
98
+ description: 'an ESM-only dependency bundles into main and preload, and RUNS (#906)',
99
+ timeout: 180000,
100
+ tests: [
101
+ {
102
+ name: 'main and preload each carry the ESM-only dependency and boot with a real import.meta.url',
103
+ run: async (ctx) => {
104
+ const tmp = await buildStagedProject();
105
+ try {
106
+ for (const name of ['main', 'preload']) {
107
+ const bundleFile = path.join(tmp, 'dist', `${name}.bundle.js`);
108
+ ctx.expect(fs.existsSync(bundleFile)).toBe(true);
109
+
110
+ const { status, stderr, result } = run(bundleFile, name);
111
+ // Without the composed define this is where it ends: createRequire
112
+ // gets `undefined` and throws before the app's first line.
113
+ ctx.expect(status).toBe(0);
114
+ ctx.expect(stderr.includes('ERR_INVALID_ARG_VALUE')).toBe(false);
115
+
116
+ ctx.expect(result.marker).toBe(NODE_MARKER);
117
+ ctx.expect(result.fileUrl.startsWith('file://')).toBe(true);
118
+ // The require the dependency opened for itself actually works.
119
+ ctx.expect(result.viaRequire).toBe(true);
120
+ }
121
+ } finally {
122
+ fs.rmSync(tmp, { recursive: true, force: true });
123
+ }
124
+ },
125
+ },
126
+ ],
127
+ });
@@ -0,0 +1,53 @@
1
+ // Build-layer tests for the time-limited command runner. The real limit is
2
+ // minutes; the mechanism is proven with a one-second one against a real child
3
+ // that would sit for 30s.
4
+
5
+ const path = require('path');
6
+ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
7
+
8
+ const { execWithLimit } = require(path.join(__dirname, '..', '..', '..', 'lib', 'sign-helpers', 'exec-with-limit.js'));
9
+
10
+ module.exports = defineCases({
11
+ type: 'group',
12
+ layer: 'build',
13
+ description: 'exec-with-limit — a child that never returns is rejected at the limit and terminated',
14
+ tests: [
15
+ {
16
+ name: 'the limit rejects on its own timer, naming the label and the seconds',
17
+ run: async (ctx) => {
18
+ const started = Date.now();
19
+
20
+ let threw;
21
+ try {
22
+ await execWithLimit('node -e "setTimeout(function () {}, 30000)"', { limitMs: 1000, label: 'signtool' });
23
+ } catch (e) { threw = e; }
24
+
25
+ ctx.expect(threw).toBeDefined();
26
+ ctx.expect(threw.message).toBe('signtool produced no verdict within 1s and was terminated');
27
+ // Rejected at the limit, not at the child's leisure.
28
+ ctx.expect(Date.now() - started).toBeLessThan(5000);
29
+ },
30
+ },
31
+ {
32
+ name: 'a child that returns in time is untouched, output and all',
33
+ run: async (ctx) => {
34
+ ctx.expect(await execWithLimit('node -e "process.stdout.write(\'ok\')"', { limitMs: 10000 })).toBe('ok');
35
+
36
+ // The child's own failure is the child's own error.
37
+ let threw;
38
+ try {
39
+ await execWithLimit('node -e "console.error(\'bad input\'); process.exit(2)"', { limitMs: 10000 });
40
+ } catch (e) { threw = e; }
41
+ ctx.expect(threw.message).toMatch(/bad input/);
42
+ },
43
+ },
44
+ {
45
+ name: 'a missing limit is a programmer error, refused up front',
46
+ run: (ctx) => {
47
+ let threw;
48
+ try { execWithLimit('node -v', {}); } catch (e) { threw = e; }
49
+ ctx.expect(threw.message).toMatch(/positive limitMs/);
50
+ },
51
+ },
52
+ ],
53
+ });
@@ -88,8 +88,8 @@ module.exports = defineCases({
88
88
  const finalizeRelease = require(path.join(__dirname, '..', '..', '..', 'commands', 'finalize-release.js'));
89
89
  const { brandRoot, targetDir } = stageBrand({
90
90
  brand: { id: 'acme' },
91
- repo: { providers: { github: { org: 'Acme-Org' } } },
92
- targets: { desktop: { releases: {} } },
91
+ repo: { provider: 'github', org: 'Acme-Org' },
92
+ targets: { desktop: { type: 'desktop', releases: {} } },
93
93
  });
94
94
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify({ name: 'acme-desktop', version: '1.2.3' }));
95
95