@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
@@ -1,69 +0,0 @@
1
- /**
2
- * Signing-artifact delivery on the desktop verbs
3
- * ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)).
4
- *
5
- * The copy itself is devkit's (`@omega.js/devkit/certs`) — the ONE delivery
6
- * step, the same rules the manager's disperse writer rides. What lives here is
7
- * the desktop FRAMING: which root holds the signing tree, the brand block the
8
- * `{brand.id}` rules resolve from, and this framework's own voice.
9
- *
10
- * The source root is the COMPANY root when the brand is company-managed (the
11
- * `.omega/company.json` stamp @omega.js/config reads), the brand root
12
- * otherwise — the same rule disperse used, read through the same one reader.
13
- * A target outside a brand has no tree and no delivery.
14
- */
15
- const jetpack = require('fs-jetpack');
16
- const { findBrandRoot, readCompanyRoot, loadConfig } = require('../vendor/config/index.js');
17
- const { deliverCerts, certsSourceDir } = require('../vendor/devkit/certs.js');
18
-
19
- /**
20
- * The root holding `.omega/certificates/apple/` for a target: the company
21
- * workspace when one is stamped, else the brand root.
22
- *
23
- * @param {string} projectDir - The target root.
24
- * @returns {string|null} The source root, or null when the target is not in a brand.
25
- */
26
- function resolveSourceRoot(projectDir) {
27
- const brandRoot = findBrandRoot(projectDir);
28
- if (!brandRoot) return null;
29
-
30
- return readCompanyRoot(brandRoot) || brandRoot;
31
- }
32
-
33
- /**
34
- * Deliver this target's Apple artifacts into its own certs dir, so signing
35
- * finds them on every verb rather than only after a manage run.
36
- *
37
- * Reports and returns; it never throws. A brandless target, a tree that was
38
- * never produced, or a missing artifact are all normal states — the signing
39
- * lookup and validate-certs are what judge them.
40
- *
41
- * @param {object} input
42
- * @param {string} input.projectDir - The target root.
43
- * @param {object} input.logger - `{ log, warn, error }`.
44
- * @returns {{ copied: string[], current: string[], missing: string[] }|null} null when there is no tree.
45
- */
46
- function deliverTargetCerts({ projectDir, logger }) {
47
- const sourceRoot = resolveSourceRoot(projectDir);
48
- if (!sourceRoot || !jetpack.exists(certsSourceDir(sourceRoot))) {
49
- return null;
50
- }
51
-
52
- let brand = null;
53
- try {
54
- brand = loadConfig(projectDir, 'desktop').config.brand || null;
55
- } catch (e) {
56
- // Unloadable config — the `{brand.id}` rules simply resolve empty; the
57
- // required rules below still deliver.
58
- }
59
-
60
- const result = deliverCerts({ sourceRoot, targetDir: projectDir, target: 'desktop', brand });
61
-
62
- if (result.copied.length > 0) {
63
- logger.log(`Delivered signing artifacts: ${result.copied.join(', ')}`);
64
- }
65
-
66
- return result;
67
- }
68
-
69
- module.exports = { deliverTargetCerts, resolveSourceRoot };
@@ -1,56 +0,0 @@
1
- // Where CSC_LINK and APPLE_API_KEY come from when nothing has answered yet
2
- // ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)).
3
- //
4
- // The manager's disperse service used to stamp both into the target's .env as
5
- // target-relative paths, and only when the file was there. No machine writes a
6
- // target .env any more, so the build derives them instead: the artifacts are
7
- // delivered into config/certs/ by every verb (deliver-certs.js), and a delivered
8
- // file that nothing has pointed at IS the answer.
9
- //
10
- // Rules, per key: an env value that is already set always wins (an explicit
11
- // answer is never overridden), and a file that is not there sets nothing (an
12
- // unset key lets electron-builder's own skip/auto-discovery logic apply — the
13
- // same reason sanitize-signing-env.js deletes empty placeholders).
14
- //
15
- // APPLE_API_KEY's filename carries the key id, so an unset APPLE_API_KEY_ID
16
- // means there is no path to derive.
17
-
18
- const path = require('node:path');
19
- const jetpack = require('fs-jetpack');
20
-
21
- const CERTS_DIR = path.join('config', 'certs');
22
- const DEVELOPER_ID_APPLICATION = 'developer-id-application.p12';
23
-
24
- /**
25
- * Set the signing paths the delivered artifacts imply. Mutates `env`.
26
- *
27
- * @param {object} input
28
- * @param {object} input.env - The env map to derive into (the build's process env).
29
- * @param {string} input.projectDir - The target root (paths are written relative to it).
30
- * @returns {Array<{ key: string, value: string }>} What was derived — empty when nothing was.
31
- */
32
- function deriveSigningEnv({ env, projectDir }) {
33
- const derived = [];
34
-
35
- const candidates = [
36
- { key: 'CSC_LINK', file: DEVELOPER_ID_APPLICATION },
37
- { key: 'APPLE_API_KEY', file: env.APPLE_API_KEY_ID ? `AuthKey_${env.APPLE_API_KEY_ID}.p8` : null },
38
- ];
39
-
40
- for (const { key, file } of candidates) {
41
- if (!file) continue;
42
- if (typeof env[key] === 'string' && env[key].trim() !== '') continue;
43
-
44
- const relative = path.join(CERTS_DIR, file);
45
- if (!jetpack.exists(path.join(projectDir, relative))) continue;
46
-
47
- env[key] = relative;
48
- derived.push({ key, value: relative });
49
- }
50
-
51
- return derived;
52
- }
53
-
54
- module.exports = deriveSigningEnv;
55
- module.exports.CERTS_DIR = CERTS_DIR;
56
- module.exports.DEVELOPER_ID_APPLICATION = DEVELOPER_ID_APPLICATION;
@@ -1,175 +0,0 @@
1
- // Where the macOS signing certificate comes from — the ONE lookup order.
2
- //
3
- // 1. CSC_LINK in the environment — an explicit answer always wins.
4
- // 2. The BRAND's gitignored signing tree:
5
- // <brandRoot>/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12
6
- // The brand root is the nearest directory at or above the target carrying a
7
- // `.omega/`. The manager's disperse service also copies this material into
8
- // each app's config/certs/; this lookup is the portable/CI path that needs
9
- // no dispersal first.
10
- // 3. The COMPANY's tree, when the brand is company-managed. A brand is NEVER
11
- // physically inside its company folder — membership is the
12
- // `.omega/company.json` stamp at the brand root, read through
13
- // @omega.js/config's readCompanyRoot (the ecosystem's ONE company rule; no
14
- // second reader lives here). The stamp's target is used as given: it is
15
- // explicit config, not discovery, so the home bound below does not apply to it.
16
- // 4. The macOS Keychain — electron-builder's own identity auto-discovery,
17
- // which is what happens when nothing sets CSC_LINK.
18
- //
19
- // Keychain stays the DEFAULT for local signed builds: a tree with no cert (every
20
- // setup that predates the certificates service) resolves to it untouched. Two
21
- // further conditions keep it there rather than handing electron-builder a file
22
- // it cannot use:
23
- // - no CSC_KEY_PASSWORD (it comes from the .env cascade, never from config), or
24
- // - a password that does not OPEN the .p12. An unopenable .p12 fails the build
25
- // outright — worse than today's behavior. The check is the read-side twin of
26
- // how the manager EXPORTS one (openssl `pkcs12 -legacy`; the legacy provider
27
- // reads modern containers too).
28
- //
29
- // The brand-root walk stops BELOW the home directory: `~/.omega` is a real
30
- // personal overlay on developer machines, and a stray tree there must never sign
31
- // every build.
32
-
33
- const { execFileSync } = require('node:child_process');
34
- const { homedir } = require('node:os');
35
- const { dirname, join } = require('node:path');
36
- const jetpack = require('fs-jetpack');
37
- const { readCompanyRoot } = require('../vendor/config/index.js');
38
-
39
- const OMEGA_DIR = '.omega';
40
- const CERT_REL = join(OMEGA_DIR, 'certificates', 'apple', 'certificates', 'DEVELOPER_ID_APPLICATION_G2.p12');
41
-
42
- /**
43
- * The brand root a target belongs to: the nearest directory at or above it
44
- * carrying a `.omega/`. Stops below the home directory.
45
- *
46
- * @param {string} from - Directory to start walking up from
47
- * @param {string} homeDir - Boundary: this directory and everything above never count
48
- * @returns {string|null}
49
- */
50
- function findBrandRoot(from, homeDir) {
51
- let dir = from;
52
-
53
- while (true) {
54
- // The home directory itself — and everything above it — is out of bounds.
55
- if (dir === homeDir) {
56
- return null;
57
- }
58
-
59
- if (jetpack.exists(join(dir, OMEGA_DIR)) === 'dir') {
60
- return dir;
61
- }
62
-
63
- const parent = dirname(dir);
64
- if (parent === dir) {
65
- return null;
66
- }
67
- dir = parent;
68
- }
69
- }
70
-
71
- /**
72
- * The signing certificate a root holds, if any.
73
- *
74
- * @param {string|null} root
75
- * @returns {string|null} - Absolute path to the .p12, or null
76
- */
77
- function certificateIn(root) {
78
- if (!root) {
79
- return null;
80
- }
81
-
82
- const candidate = join(root, CERT_REL);
83
- return jetpack.exists(candidate) === 'file' ? candidate : null;
84
- }
85
-
86
- /**
87
- * Does this password actually open the container?
88
- *
89
- * Two openssl families answer differently: OpenSSL 3 needs `-legacy` to read
90
- * the legacy-format containers the manager exports (macOS `security import`
91
- * needs that format), while stock macOS /usr/bin/openssl is LibreSSL, which
92
- * rejects the `-legacy` FLAG but reads legacy containers natively. A flag
93
- * rejection is retried without it; only a real password/container failure
94
- * counts as "does not open".
95
- *
96
- * @param {string} p12Path - Path to the .p12
97
- * @param {string} password - Candidate password
98
- * @returns {{ opens: boolean, tooling?: string }} - tooling set when openssl
99
- * itself was unavailable/unusable, so the caller can say so instead of
100
- * blaming the password
101
- */
102
- function opensWith(p12Path, password) {
103
- // Password rides an env var — never argv, never a shell string.
104
- const run = (args) => execFileSync(
105
- 'openssl',
106
- ['pkcs12', ...args, '-in', p12Path, '-nokeys', '-noout', '-passin', 'env:OMEGA_P12_PASSWORD'],
107
- { stdio: 'pipe', env: { ...process.env, OMEGA_P12_PASSWORD: password } },
108
- );
109
-
110
- try {
111
- run(['-legacy']);
112
- return { opens: true };
113
- } catch (error) {
114
- if (error.code === 'ENOENT') {
115
- return { opens: false, tooling: 'openssl is not available' };
116
- }
117
-
118
- if (String(error.stderr || '').includes('unknown option')) {
119
- try {
120
- run([]);
121
- return { opens: true };
122
- } catch (retryError) {
123
- if (retryError.code === 'ENOENT') {
124
- return { opens: false, tooling: 'openssl is not available' };
125
- }
126
- return { opens: false };
127
- }
128
- }
129
-
130
- return { opens: false };
131
- }
132
- }
133
-
134
- /**
135
- * Resolve which certificate source a signed build should use.
136
- *
137
- * @param {object} options
138
- * @param {string} options.projectRoot - The desktop app's root
139
- * @param {object} [options.env] - Environment to read (default process.env)
140
- * @param {string} [options.homeDir] - Walk boundary (default os.homedir())
141
- * @returns {{ source: 'env'|'certificates'|'keychain', cscLink: string|null, reason: string }}
142
- */
143
- function resolveSigningCert({ projectRoot, env = process.env, homeDir = homedir() }) {
144
- const cscLink = typeof env.CSC_LINK === 'string' ? env.CSC_LINK.trim() : '';
145
-
146
- if (cscLink) {
147
- return { source: 'env', cscLink, reason: 'CSC_LINK is set' };
148
- }
149
-
150
- // The brand's own tree first, then the company's — reached ONLY through the
151
- // brand's .omega/company.json stamp, never by walking up into a parent folder.
152
- const brandRoot = findBrandRoot(projectRoot, homeDir);
153
- const found = certificateIn(brandRoot) || certificateIn(brandRoot && readCompanyRoot(brandRoot));
154
-
155
- if (!found) {
156
- return { source: 'keychain', cscLink: null, reason: 'no .omega/certificates signing tree — using Keychain identity discovery' };
157
- }
158
-
159
- if (!env.CSC_KEY_PASSWORD) {
160
- return { source: 'keychain', cscLink: null, reason: `found ${found} but CSC_KEY_PASSWORD is unset — using Keychain identity discovery` };
161
- }
162
-
163
- const check = opensWith(found, env.CSC_KEY_PASSWORD);
164
- if (check.tooling) {
165
- return { source: 'keychain', cscLink: null, reason: `${check.tooling} — cannot verify ${found}; using Keychain identity discovery` };
166
- }
167
- if (!check.opens) {
168
- return { source: 'keychain', cscLink: null, reason: `CSC_KEY_PASSWORD does not open ${found} — using Keychain identity discovery` };
169
- }
170
-
171
- return { source: 'certificates', cscLink: found, reason: 'signing certificate from the brand .omega/certificates tree' };
172
- }
173
-
174
- module.exports = resolveSigningCert;
175
- module.exports.CERT_REL = CERT_REL;
@@ -1,110 +0,0 @@
1
- /**
2
- * @omega.js/config desktop-artifacts — the SINGLE naming rule for a desktop
3
- * target's release assets ([#620](https://github.com/Omega-JS-Stack/omega/issues/620)).
4
- *
5
- * Two readers, one rule: @omega.js/desktop's `gulp/build-config` writes these
6
- * names into `dist/electron-builder.yml`'s artifactName templates, and
7
- * [site-global.js](site-global.js) derives the website's direct-download URLs
8
- * from them (`<releasesUrl>/download/<name>`). A second spelling anywhere is a
9
- * dead download button on a live site, so the derivation lives here and both
10
- * sides call it.
11
- *
12
- * Names carry NO version. That is the whole point: GitHub's
13
- * `/releases/latest/download/<asset>` only resolves when the asset name is the
14
- * same in every release, so releasing a desktop version never touches the
15
- * website and the published URLs never change. electron-updater is unaffected —
16
- * it reads the feed files (`latest-mac.yml`, `latest.yml`), which name whatever
17
- * artifact the build produced.
18
- *
19
- * Nothing puts an arch back into a name that omits one: electron-builder's
20
- * macro expander only REPLACES a `${arch}` token where it appears. So these
21
- * names support exactly the default build — a UNIVERSAL mac, a SINGLE linux
22
- * arch, and windows' multi-arch NSIS installer (which merges every arch into
23
- * one file anyway). @omega.js/desktop's build-config refuses any other arch set
24
- * rather than let two builds collide on one name; per-arch names are a future
25
- * additive change.
26
- */
27
-
28
- // Every artifact a release publishes as a user-facing download, per platform,
29
- // in the order the platform offers them — the FIRST is the platform's default
30
- // (`/download/<platform>` leads with it, legacy UJM parity). The artifact keys
31
- // ARE the website's URL vocabulary (`/download/<platform>/<artifact>`).
32
- //
33
- // Deliberately absent: the mac auto-update ZIP (electron-updater fetches it
34
- // from the feed; nobody links it) and the snap (the Snap Store publishes it,
35
- // so it is never a release asset).
36
- const DESKTOP_ARTIFACTS = {
37
- mac: { universal: { ext: 'dmg' } },
38
- windows: { universal: { ext: 'exe' } },
39
- linux: { debian: { ext: 'deb' }, appimage: { ext: 'AppImage' } },
40
- };
41
-
42
- /**
43
- * The product name a desktop target packages under — the same derivation
44
- * @omega.js/desktop's `Manager.getConfig()` applies (`app.productName` ←
45
- * `brand.name`), so the website reads the name the packager actually used.
46
- *
47
- * @param {object} config - Desktop-shaped config (`{ app, brand }`).
48
- * @returns {string} Product name ('' when neither key resolves).
49
- */
50
- function desktopProductName(config) {
51
- return config?.app?.productName || config?.brand?.name || '';
52
- }
53
-
54
- /**
55
- * A product name reduced to filename-safe characters. electron-builder's
56
- * `${productName}` keeps spaces, which become dots in NSIS output and behave
57
- * inconsistently across targets; hyphens are consistent everywhere.
58
- *
59
- * @param {string} productName - Raw product name ("Deployment Playground").
60
- * @returns {string} Sanitized name ("Deployment-Playground"), '' when empty.
61
- */
62
- function sanitizeProductName(productName) {
63
- return String(productName || '')
64
- .replace(/[^A-Za-z0-9._-]+/g, '-')
65
- .replace(/-+/g, '-')
66
- .replace(/^-|-$/g, '');
67
- }
68
-
69
- /**
70
- * One artifact's versionless filename: `<Product>-<platform>-<artifact>.<ext>`.
71
- *
72
- * @param {string} productName - Raw product name (sanitized here).
73
- * @param {string} platform - Platform key (mac, windows, linux).
74
- * @param {string} artifact - Artifact key (universal, debian, appimage, …).
75
- * @param {string} [ext] - Extension override — pass electron-builder's literal
76
- * `${ext}` to get a TEMPLATE that covers every target sharing the platform's
77
- * fallback (mac's dmg + auto-update zip).
78
- * @returns {string} The filename ('' with no product name to build it from).
79
- */
80
- function desktopArtifactName(productName, platform, artifact, ext) {
81
- const product = sanitizeProductName(productName);
82
- const extension = ext || DESKTOP_ARTIFACTS[platform]?.[artifact]?.ext;
83
- if (!product || !extension) return '';
84
-
85
- return `${product}-${platform}-${artifact}.${extension}`;
86
- }
87
-
88
- /**
89
- * Every published artifact's filename, keyed platform → artifact, in offer
90
- * order.
91
- *
92
- * @param {string} productName - Raw product name.
93
- * @returns {object} `{ mac: { universal: 'App-mac-universal.dmg' }, … }`, or
94
- * `{}` when there is no product name to derive from.
95
- */
96
- function desktopArtifactNames(productName) {
97
- if (!sanitizeProductName(productName)) return {};
98
-
99
- const names = {};
100
- for (const [platform, artifacts] of Object.entries(DESKTOP_ARTIFACTS)) {
101
- names[platform] = {};
102
- for (const artifact of Object.keys(artifacts)) {
103
- names[platform][artifact] = desktopArtifactName(productName, platform, artifact);
104
- }
105
- }
106
-
107
- return names;
108
- }
109
-
110
- module.exports = { DESKTOP_ARTIFACTS, desktopProductName, sanitizeProductName, desktopArtifactName, desktopArtifactNames };
@@ -1,208 +0,0 @@
1
- /**
2
- * Multi-instance targets — the normalization trick and the target-dir mapping
3
- * (_attic/plans/multi-instance-targets.md, shape ratified by Ian 2026-07-20).
4
- *
5
- * `targets.<type>` may be an object (today's single-instance shape) or an
6
- * ARRAY of id'd instance entries. Normalization is the whole mechanism: a
7
- * single object normalizes to `[{ id: 'main', ...entry }]` internally, so
8
- * every consumer of target config iterates instances and the single-instance
9
- * world is just length 1 — zero breaking change for existing brands.
10
- *
11
- * Target-dir mapping: the `main` instance (or the single-object form) lives in
12
- * `targets/<canonical dir>` (targets/website, unchanged); any other id lives in
13
- * `targets/<canonical dir>-<id>` (targets/website-admin). The inverse walk resolves
14
- * WHICH instance a target dir is, and loadConfig/composeTargetConfig use it to
15
- * pick the instance entry as the target layer of the merge chain.
16
- *
17
- * This module is the ONE home for the instance iteration mechanism (mirrored-
18
- * implementation rule) — frameworks and the manager import it, never copy it.
19
- * The canonical dir names moved here from the manager's config.js so the
20
- * mapping and its inverse walk share a single SSOT.
21
- */
22
-
23
- const path = require('node:path');
24
-
25
- const { isPlainObject } = require('./merge.js');
26
-
27
- // targets/<dir> → target mapping when the target dir doesn't declare its target
28
- // in its own omega.json5. Exact match or `<name>-<id>` suffix (targets/website-admin →
29
- // web, instance admin). Moved here from @omega.js/manager's config.js (which
30
- // re-exports it) so the target-dir walk has one home.
31
- const DIR_TARGETS = {
32
- website: 'web',
33
- backend: 'backend',
34
- desktop: 'desktop',
35
- extension: 'extension',
36
- mobile: 'mobile',
37
- };
38
-
39
- // Inverse: canonical target dir per target (targets/website for web, …)
40
- const TARGET_DIRS = Object.fromEntries(
41
- Object.entries(DIR_TARGETS).map(([dir, target]) => [target, dir]),
42
- );
43
-
44
- // The conventional primary instance id — the single-object form normalizes
45
- // to it, and it maps to the canonical (un-suffixed) target dir.
46
- const MAIN_INSTANCE = 'main';
47
-
48
- // Instance ids are target-dir suffixes, so they must be dir-safe slugs
49
- const INSTANCE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
50
-
51
- /**
52
- * Normalize a `targets.<type>` entry to the instances array. The whole trick:
53
- * a single object becomes `[{ id: 'main', ...entry }]` (an authored `id`
54
- * inside the object wins); an array passes through as-is — the validator, not
55
- * this function, enforces that array entries carry ids.
56
- * @param {object|Array|null|undefined} entry - The raw targets.<type> value.
57
- * @returns {Array<object>} Instance entries (shallow copies); [] when absent.
58
- */
59
- function normalizeTargetInstances(entry) {
60
- if (entry === undefined || entry === null) return [];
61
- if (Array.isArray(entry)) return entry.map((instance) => (isPlainObject(instance) ? { ...instance } : instance));
62
- return [{ id: MAIN_INSTANCE, ...entry }];
63
- }
64
-
65
- /**
66
- * Target dir name → instance id for a target. The inverse walk of the mapping:
67
- * the canonical dir (targets/website) is `main`; a `<canonical>-<id>` suffix
68
- * names the instance (targets/website-admin → admin). Unconventional dir names
69
- * resolve to `main` — today's behavior for dirs like targets/api.
70
- * @param {string} dirName - The target directory basename.
71
- * @param {string} target - Canonical target name ('web', 'backend', ...).
72
- * @returns {string} The instance id.
73
- */
74
- function instanceIdFromDirName(dirName, target) {
75
- const canonical = TARGET_DIRS[target] || target;
76
- if (dirName.startsWith(`${canonical}-`)) {
77
- return dirName.slice(canonical.length + 1);
78
- }
79
- return MAIN_INSTANCE;
80
- }
81
-
82
- /**
83
- * Instance id → its target dir name (`main` → the canonical dir, anything else
84
- * → `<canonical>-<id>`).
85
- * @param {string} target - Canonical target name.
86
- * @param {string} id - Instance id.
87
- * @returns {string} The target directory basename.
88
- */
89
- function instanceTargetDir(target, id) {
90
- const canonical = TARGET_DIRS[target] || target;
91
- return id === MAIN_INSTANCE ? canonical : `${canonical}-${id}`;
92
- }
93
-
94
- /**
95
- * Resolve THIS project dir's instance id for a target: TARGET_SUBDIR dirs
96
- * (functions/, dist/) normalize up to their target root, and only targets inside a
97
- * brand monorepo resolve through the dir-name walk — a standalone project
98
- * (whose dir name is arbitrary) is always `main`.
99
- * @param {string} projectDir - Target root (or one of its TARGET_SUBDIRS).
100
- * @param {string} target - Canonical target name.
101
- * @returns {string} The instance id.
102
- */
103
- function targetInstance(projectDir, target) {
104
- // Local require to avoid a load-time cycle (load.js requires this module)
105
- const { findBrandRoot, TARGET_SUBDIRS } = require('./load.js');
106
-
107
- let targetRoot = path.resolve(projectDir);
108
- if (TARGET_SUBDIRS.includes(path.basename(targetRoot))) {
109
- targetRoot = path.dirname(targetRoot);
110
- }
111
-
112
- if (!findBrandRoot(targetRoot)) return MAIN_INSTANCE;
113
- return instanceIdFromDirName(path.basename(targetRoot), target);
114
- }
115
-
116
- /**
117
- * The merge-chain layer for one instance: the single-object form applies to
118
- * EVERY target dir of the type (today's semantics, unchanged); the array form
119
- * applies only to the exact-id match — no match means no instance layer
120
- * (shared config alone). The `id` key is bookkeeping, never config — it is
121
- * stripped from the returned entry.
122
- * @param {object|Array|null|undefined} entry - The raw targets.<type> value.
123
- * @param {string} instanceId - The instance to resolve.
124
- * @returns {object|null} The instance's merge layer, or null.
125
- */
126
- function resolveInstanceEntry(entry, instanceId) {
127
- if (entry === undefined || entry === null) return null;
128
- if (!Array.isArray(entry)) return entry;
129
-
130
- const hit = entry.find((instance) => isPlainObject(instance) && instance.id === instanceId);
131
- if (!hit) return null;
132
-
133
- const { id, ...rest } = hit;
134
- return rest;
135
- }
136
-
137
- /**
138
- * Deterministic dev-port offset for an instance: its position in the
139
- * normalized instances array (main-or-object form = 0), so N instances get N
140
- * side-by-side ports off the same classic base. Unknown ids offset 0 — the
141
- * N7 bump-if-taken allocator still guarantees a free port.
142
- * @param {object|Array|null|undefined} entry - The raw targets.<type> value.
143
- * @param {string} instanceId - The instance id.
144
- * @returns {number} Zero-based offset.
145
- */
146
- function instancePortOffset(entry, instanceId) {
147
- const index = normalizeTargetInstances(entry).findIndex((instance) => isPlainObject(instance) && instance.id === instanceId);
148
- return index > 0 ? index : 0;
149
- }
150
-
151
- /**
152
- * The host `brand.url` names, EXACTLY as it states it: a `www.` label is kept
153
- * (a www brand is a www brand), any path is dropped, a port survives. A
154
- * scheme-less value is read as https.
155
- * @param {string} url - A brand URL.
156
- * @returns {string} The host, or '' when the value is not a URL.
157
- */
158
- function brandHost(url) {
159
- try {
160
- return new URL(url.includes('://') ? url : `https://${url}`).host;
161
- } catch {
162
- return '';
163
- }
164
- }
165
-
166
- /**
167
- * The live URL of an instance: its entry's `url` (the spec's per-instance
168
- * key), an instance-scoped `brand.url` override, then, for every id but
169
- * `main`, the id AS A SUBDOMAIN of the brand host (#588), and the
170
- * brand-shared brand.url for `main`. The manager's per-instance live checks,
171
- * the resolved config's top-level `url` and site-global all read this.
172
- *
173
- * The id-is-the-subdomain default is what makes `web: [{ id: 'main' },
174
- * { id: 'admin' }]` a complete declaration; an explicit `url` overrides it for
175
- * a custom host (`{ id: 'store', url: 'https://shop.acme.com' }`). Nothing is
176
- * half-derived: no usable brand.url means null for every id, never
177
- * `https://admin.`.
178
- * @param {object|Array|null|undefined} entry - The raw targets.<type> value.
179
- * @param {string} instanceId - The instance id.
180
- * @param {object} config - The (resolved) config carrying brand.url.
181
- * @returns {string|null} The instance's URL, or null when none is known.
182
- */
183
- function resolveInstanceUrl(entry, instanceId, config) {
184
- const instance = resolveInstanceEntry(entry, instanceId);
185
- if (instance?.url) return instance.url;
186
- if (instance?.brand?.url) return instance.brand.url;
187
-
188
- const brandUrl = config?.brand?.url;
189
- if (!brandUrl) return null;
190
- if (instanceId === MAIN_INSTANCE) return brandUrl;
191
-
192
- const host = brandHost(brandUrl);
193
- return host ? `https://${instanceId}.${host}` : null;
194
- }
195
-
196
- module.exports = {
197
- DIR_TARGETS,
198
- TARGET_DIRS,
199
- MAIN_INSTANCE,
200
- INSTANCE_ID_PATTERN,
201
- normalizeTargetInstances,
202
- instanceIdFromDirName,
203
- instanceTargetDir,
204
- targetInstance,
205
- resolveInstanceEntry,
206
- instancePortOffset,
207
- resolveInstanceUrl,
208
- };