@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
@@ -14,7 +14,7 @@
14
14
  const path = require('path');
15
15
  const jetpack = require('fs-jetpack');
16
16
  const yaml = require('js-yaml');
17
- const { deepMerge, desktopArtifactName, sanitizeProductName, releasesRepo } = require('../../vendor/config/index.js');
17
+ const { deepMerge, enabledFormats, desktopArtifactName, sanitizeProductName, releasesRepo } = require('../../vendor/config/index.js');
18
18
  const Manager = new (require('../../build.js'));
19
19
 
20
20
  const { writeMacEntitlements } = require('../../lib/sign-helpers/entitlements.js');
@@ -40,7 +40,7 @@ module.exports = function buildConfig(done) {
40
40
  // 2. Resolve + copy icons (3-tier waterfall) into dist/config/icons/.
41
41
  const emDefaultsRoot = path.join(__dirname, '..', '..', 'defaults', 'config');
42
42
  const icons = await resolveAndCopy({ config, projectRoot, distRoot, emDefaultsRoot });
43
- logger.log(`resolved icons: mac=${Object.keys(icons.macos).length}, win=${Object.keys(icons.windows).length}, linux=${Object.keys(icons.linux).length}`);
43
+ logger.log(`resolved icons: mac=${Object.keys(icons.mac).length}, windows=${Object.keys(icons.windows).length}, linux=${Object.keys(icons.linux).length}`);
44
44
 
45
45
  // Build the full config object from @omega.js/desktop defaults + consumer overrides.
46
46
  let builderConfig = baseConfig(config, { entitlementsPath, icons, distRoot, projectRoot });
@@ -79,12 +79,14 @@ module.exports = function buildConfig(done) {
79
79
  builderConfig.publish = publish;
80
80
  logger.log(`releases → publish block: github ${publish.owner}/${publish.repo}`);
81
81
  } else if (config.releases?.enabled !== false) {
82
- logger.warn('Could not address the releases repo (no github org and no releases.owner); leaving publish block off.');
82
+ logger.warn('Could not address the releases repo (set repo.org and brand.id); leaving publish block off.');
83
83
  }
84
84
 
85
- // Inject afterSign → @omega.js/desktop's built-in notarize hook.
86
- builderConfig.afterSign = require.resolve('@omega.js/desktop/hooks/notarize');
87
- logger.log(`afterSign → ${builderConfig.afterSign}`);
85
+ // Inject the two notarize hooks (the .app, then each .dmg).
86
+ for (const [hook, file] of Object.entries(notarizeHooks())) {
87
+ builderConfig[hook] = file;
88
+ logger.log(`${hook} → ${file}`);
89
+ }
88
90
 
89
91
  // Apply consumer overrides last so they win.
90
92
  if (config.electronBuilder && typeof config.electronBuilder === 'object') {
@@ -92,6 +94,10 @@ module.exports = function buildConfig(done) {
92
94
  logger.log('Applied electronBuilder overrides from omega.json5');
93
95
  }
94
96
 
97
+ // The deb target's metadata, checked AFTER the overrides so a consumer that
98
+ // spells `linux.maintainer` itself passes (#872).
99
+ assertLinuxPackageMetadata(builderConfig);
100
+
95
101
  // Serialize to YAML and write.
96
102
  const yml = yaml.dump(builderConfig, { lineWidth: -1, noRefs: true });
97
103
  jetpack.write(distPath, yml);
@@ -99,6 +105,24 @@ module.exports = function buildConfig(done) {
99
105
  }).then(() => done(), done);
100
106
  };
101
107
 
108
+ // OMEGA's format words → electron-builder's target names (#867). THE one point
109
+ // where the foreign spelling is used: `appimage` is `AppImage` to
110
+ // electron-builder, and its per-target config block is `appImage` again.
111
+ const BUILDER_TARGETS = {
112
+ dmg: 'dmg',
113
+ nsis: 'nsis',
114
+ deb: 'deb',
115
+ appimage: 'AppImage',
116
+ snap: 'snap',
117
+ };
118
+
119
+ // A platform's install-knob block. `platforms.<platform>: false` (the whole
120
+ // platform dropped) and an absent block read the same here: defaults, and no
121
+ // target, because enabledFormats already said it ships nothing.
122
+ function platformBlock(declaration) {
123
+ return declaration && typeof declaration === 'object' ? declaration : {};
124
+ }
125
+
102
126
  // Generic-category → per-platform mapping. Consumer sets `app.category` to one of these
103
127
  // keys; @omega.js/desktop emits the corresponding mac UTI string and Linux freedesktop category.
104
128
  //
@@ -153,10 +177,21 @@ function baseConfig(config, extras = {}) {
153
177
  const languages = Array.isArray(config.app.languages) ? config.app.languages : ['en'];
154
178
  const darkModeSupport = config.app.darkModeSupport !== false; // default true
155
179
 
156
- // Per-platform config blocks. Each is fully optional — every key has a default.
157
- const macTargetCfg = config.platforms?.mac || {};
158
- const winTargetCfg = config.platforms?.win || {};
159
- const linuxTargetCfg = config.platforms?.linux || {};
180
+ // What this brand SHIPS, from the ONE declaration (#867):
181
+ // `platforms.<mac|windows|linux>.formats.<dmg|nsis|deb|appimage|snap>`, where
182
+ // presence is the switch and every format defaults ON. @omega.js/config's
183
+ // format table is the vocabulary; the electron-builder spelling of each
184
+ // format is translated at ONE point below (BUILDER_TARGETS).
185
+ const shipped = enabledFormats(config, 'desktop');
186
+ const ships = (platform, format) => shipped.some((entry) => entry.platform === platform && entry.format === format);
187
+ const formatOptions = (platform, format) => shipped.find((entry) => entry.platform === platform && entry.format === format)?.options || {};
188
+
189
+ // Per-platform config blocks (the install knobs beside `formats`). Each is
190
+ // fully optional: every key has a default. A platform switched off with
191
+ // `false` reads as an empty block here and ships no target below.
192
+ const macTargetCfg = platformBlock(config.platforms?.mac);
193
+ const winTargetCfg = platformBlock(config.platforms?.windows);
194
+ const linuxTargetCfg = platformBlock(config.platforms?.linux);
160
195
 
161
196
  const macArch = Array.isArray(macTargetCfg.arch) && macTargetCfg.arch.length ? macTargetCfg.arch : ['universal'];
162
197
  const winArch = Array.isArray(winTargetCfg.arch) && winTargetCfg.arch.length ? winTargetCfg.arch : ['x64', 'ia32'];
@@ -174,20 +209,18 @@ function baseConfig(config, extras = {}) {
174
209
  const nsisRunAfterFinish = winTargetCfg.runAfterFinish !== false;
175
210
  const nsisPerMachine = winTargetCfg.perMachine === true;
176
211
 
177
- // Snap publishing — opt-in via explicit `platforms.linux.snap.enabled: true` in
178
- // config. The framework scaffold ships with that field set to true by default
179
- // (so new consumers get snap publishing out of the box once their credentials
180
- // are wired up), but if the field is missing entirely we default to OFF — that
181
- // way callers who never knew about snap don't suddenly start emitting a snap
182
- // target. Even when enabled, the snap target is auto-skipped when
183
- // SNAPCRAFT_STORE_CREDENTIALS isn't set, so a fresh project doesn't fail CI
184
- // before the user wires up snapcraft auth.
185
- const snapCfg = linuxTargetCfg.snap || {};
186
- const snapConfigEnabled = snapCfg.enabled === true;
212
+ // Snap publishing rides the declaration like every other format
213
+ // (`platforms.linux.formats.snap`, on unless dropped with `false`), and its
214
+ // settings live inside it. It is still auto-skipped when
215
+ // SNAPCRAFT_STORE_CREDENTIALS is absent, so a brand that has not run the
216
+ // manage walk's Snap ask yet gets a clean .deb + .AppImage build instead of a
217
+ // CI failure.
218
+ const snapCfg = formatOptions('linux', 'snap');
219
+ const snapConfigEnabled = ships('linux', 'snap');
187
220
  const haveSnapCreds = !!process.env.SNAPCRAFT_STORE_CREDENTIALS;
188
221
  const snapEnabled = snapConfigEnabled && haveSnapCreds;
189
222
  if (snapConfigEnabled && !haveSnapCreds) {
190
- logger.log('Snap target enabled in config but SNAPCRAFT_STORE_CREDENTIALS not set — skipping snap target. Run `snapcraft export-login -` and add to .env to enable.');
223
+ logger.log('This build ships the snap (platforms.linux.formats.snap, on unless dropped with `false`) but SNAPCRAFT_STORE_CREDENTIALS is not set, skipping the snap target. Run `npx omega manage` (it asks) or `snapcraft export-login -` and add the blob to .env.');
191
224
  }
192
225
 
193
226
  const { entitlementsPath, icons, distRoot, projectRoot } = extras;
@@ -201,31 +234,53 @@ function baseConfig(config, extras = {}) {
201
234
  return abs;
202
235
  };
203
236
 
204
- // Artifact filenames come from @omega.js/config's desktop-artifacts — the ONE
237
+ // Artifact filenames come from @omega.js/config's platforms.js: the ONE
205
238
  // naming rule (#620), shared with the website's direct-download URLs
206
239
  // (`site.targets.desktop.downloads`). They carry no version, which is what
207
240
  // makes `/releases/latest/download/<asset>` a link that never changes, and
208
241
  // they sanitize the productName (electron-builder's `${productName}` keeps
209
- // spaces, which become dots in NSIS output). Passing electron-builder's
210
- // literal `${ext}` yields a TEMPLATE, for the platform fallbacks that serve
211
- // more than one target (mac's dmg + auto-update zip).
212
- const artifactTemplate = (platform, artifact) => desktopArtifactName(productName, platform, artifact, '${ext}');
242
+ // spaces, which become dots in NSIS output).
213
243
  const safeProductName = sanitizeProductName(productName);
214
244
 
215
- // Build linux target list — `deb` + `AppImage` always; `snap` if enabled.
216
- const linuxTargets = [
217
- { target: 'deb', arch: linuxArch },
218
- { target: 'AppImage', arch: linuxArch },
219
- ];
220
- if (snapEnabled) {
221
- linuxTargets.push({ target: 'snap', arch: linuxArch });
222
- }
245
+ // The two METADATA facts the .deb target requires, both from the BRAND
246
+ // ([#872](https://github.com/Omega-JS-Stack/omega/issues/872)). electron-builder
247
+ // reads the homepage off the app's package.json, never off its own config, and
248
+ // `extraMetadata` is the block it merges into that manifest at build time; the
249
+ // maintainer is `linux.maintainer`, else the manifest's `author` name + email.
250
+ // Missing, they fail the deb build LATE, after the AppImage has already
251
+ // published, which is what run 34584322778 did. Absent values are left OUT
252
+ // here (never an empty string) and named by assertLinuxPackageMetadata below.
253
+ const homepage = trimmed(config.brand.url);
254
+ const supportEmail = trimmed(config.brand.contact?.email);
255
+ const maintainer = supportEmail ? `${config.brand.name || productName} <${supportEmail}>` : '';
256
+
257
+ // The target lists, straight off the declaration. The snap is the one format
258
+ // with a second gate (its credentials, above); everything else ships because
259
+ // the brand declared it.
260
+ const linuxTargets = shipped
261
+ .filter((entry) => entry.platform === 'linux' && (entry.format !== 'snap' || snapEnabled))
262
+ .map((entry) => ({ target: BUILDER_TARGETS[entry.format], arch: linuxArch }));
263
+
264
+ // mac ships the dmg the site links plus the auto-update ZIP electron-updater
265
+ // fetches from the feed. The zip is NOT a declared format (nobody links it),
266
+ // so it rides the dmg leg: no dmg, no mac build at all.
267
+ const macTargets = ships('mac', 'dmg')
268
+ ? [{ target: 'dmg', arch: macArch }, { target: 'zip', arch: macArch }]
269
+ : [];
270
+
271
+ const winTargets = shipped
272
+ .filter((entry) => entry.platform === 'windows')
273
+ .map((entry) => ({ target: BUILDER_TARGETS[entry.format], arch: winArch }));
223
274
 
224
275
  const out = {
225
276
  appId,
226
277
  productName,
227
278
  copyright,
228
279
 
280
+ // Merged into the app's package.json for the build: the only way to hand
281
+ // electron-builder a homepage, which the .deb target requires (#872).
282
+ ...(homepage ? { extraMetadata: { homepage } } : {}),
283
+
229
284
  directories: {
230
285
  output: 'release',
231
286
  buildResources: 'config', // dist/config/ (relative to dist/electron-builder.yml)
@@ -243,6 +298,17 @@ function baseConfig(config, extras = {}) {
243
298
  // the file content differs between the x64 and arm64 builds.
244
299
  '!logs/**',
245
300
  '!**/logs/**',
301
+ // Scratch and state dirs are never app content (#866): the boot runner stages
302
+ // .omega/test-app with symlinks into the target, and the packager followed
303
+ // them (dangling after a folder rename). src/ stays: the runtime reads
304
+ // src/integrations/* from the app root. Root-anchored on purpose: these dirs
305
+ // exist only at the target root (logs/ above also appears inside node_modules).
306
+ '!.omega/**',
307
+ '!.claude/**',
308
+ '!.temp/**',
309
+ '!.cache/**',
310
+ '!.gh-runners/**',
311
+ '!test/**',
246
312
  ],
247
313
 
248
314
  asar: true,
@@ -255,29 +321,31 @@ function baseConfig(config, extras = {}) {
255
321
  // Silicon. Trade-off: ~2x file size (~225MB vs ~117MB single-arch), ~2x mac
256
322
  // build time (electron-builder builds both archs then stitches them with lipo).
257
323
  // Win: one user-facing download, no "which one do I pick?" choice for end users.
258
- target: [
259
- { target: 'dmg', arch: macArch },
260
- { target: 'zip', arch: macArch },
261
- ],
324
+ target: macTargets,
262
325
  hardenedRuntime: true,
263
326
  gatekeeperAssess: false,
264
327
  notarize: false, // notarization runs via afterSign hook
265
- // mac.artifactName is the FALLBACK template for mac targets that don't
266
- // have their own, and it serves BOTH mac targets: the .dmg the site links
267
- // (`Product-mac-universal.dmg`) and the .zip electron-updater fetches
268
- // from the feed (`Product-mac-universal.zip`). One template, so the dmg
269
- // needs no override of its own. No ${arch}: with arch=universal there is
270
- // one artifact per target — and a non-universal mac arch is refused
271
- // outright (assertArchRules), because nothing appends the arch back.
272
- artifactName: artifactTemplate('mac', 'universal'),
328
+ // mac.artifactName is the FALLBACK for mac targets with no name of their
329
+ // own, which is the auto-update .zip: it carries no format segment
330
+ // because it is not a format anyone declares or links (the feed names
331
+ // it). The .dmg has its own name below, the one the site links. No
332
+ // ${arch}: with arch=universal there is one artifact per target, and a
333
+ // non-universal mac arch is refused outright (assertArchRules) because
334
+ // nothing appends the arch back.
335
+ artifactName: `${safeProductName}-mac.\${ext}`,
273
336
  },
274
337
 
275
- dmg: {},
338
+ dmg: {
339
+ artifactName: desktopArtifactName(productName, 'mac', 'dmg'),
340
+ // Signed with the same Developer ID identity as the app: Gatekeeper
341
+ // assesses a downloaded image by its OWN signature (the notarize-artifacts
342
+ // hook's `--context context:primary-signature`), and electron-builder
343
+ // leaves the image unsigned unless told (#891).
344
+ sign: true,
345
+ },
276
346
 
277
347
  win: {
278
- target: [
279
- { target: 'nsis', arch: winArch },
280
- ],
348
+ target: winTargets,
281
349
  signtoolOptions: {
282
350
  signingHashAlgorithms: ['sha256'],
283
351
  },
@@ -285,9 +353,10 @@ function baseConfig(config, extras = {}) {
285
353
  },
286
354
 
287
355
  nsis: {
288
- // ONE installer for every winArch (electron-builder merges them), so the
289
- // name says `universal` like the site's `/download/windows/universal`.
290
- artifactName: artifactTemplate('windows', 'universal'),
356
+ // ONE installer for every winArch (electron-builder merges them), which
357
+ // is why the name carries no arch: it is the file `/download/windows/nsis`
358
+ // hands over.
359
+ artifactName: desktopArtifactName(productName, 'windows', 'nsis'),
291
360
  oneClick: nsisOneClick,
292
361
  perMachine: nsisPerMachine,
293
362
  // `createDesktopShortcut: 'always'` ensures the icon is created even when the
@@ -302,6 +371,7 @@ function baseConfig(config, extras = {}) {
302
371
  linux: {
303
372
  target: linuxTargets,
304
373
  category: category.linux,
374
+ ...(maintainer ? { maintainer } : {}),
305
375
  // The FALLBACK, which only the snap reaches — deb and AppImage are the
306
376
  // two published artifacts and carry their own names below. The snap is
307
377
  // never a release asset (the Snap Store publishes it), so it keeps ${arch}.
@@ -312,7 +382,7 @@ function baseConfig(config, extras = {}) {
312
382
  // linux.artifactName otherwise, and one template cannot say both). One
313
383
  // target apiece, so these are the finished filenames, not templates.
314
384
  deb: {
315
- artifactName: desktopArtifactName(productName, 'linux', 'debian'),
385
+ artifactName: desktopArtifactName(productName, 'linux', 'deb'),
316
386
  },
317
387
 
318
388
  appImage: {
@@ -350,8 +420,8 @@ function baseConfig(config, extras = {}) {
350
420
  }
351
421
 
352
422
  // Wire icons. electron-builder resolves these as paths relative to the cwd it was invoked from (projectRoot).
353
- if (icons?.macos?.app) out.mac.icon = rel(icons.macos.app);
354
- if (icons?.macos?.dmg) out.dmg.background = rel(icons.macos.dmg);
423
+ if (icons?.mac?.app) out.mac.icon = rel(icons.mac.app);
424
+ if (icons?.mac?.dmg) out.dmg.background = rel(icons.mac.dmg);
355
425
  if (icons?.windows?.app) out.win.icon = rel(icons.windows.app);
356
426
  if (icons?.linux?.app) out.linux.icon = rel(icons.linux.app);
357
427
 
@@ -405,7 +475,7 @@ function assertArchRules(arch, options) {
405
475
 
406
476
  // The electron-builder `publish` block: the brand's ONE public releases repo,
407
477
  // resolved by @omega.js/config's releasesRepo (`<brand.id>-releases` under the
408
- // brand repo owner unless the config names another). Config-only on purpose:
478
+ // brand's org, #883: never a typed name). Config-only on purpose:
409
479
  // this address is baked into app-update.yml and polled by every installed copy
410
480
  // forever, and a brand-monorepo app's git remote is the repo it is NESTED in
411
481
  // (the playground inside the framework monorepo), which would bake a feed that
@@ -416,12 +486,26 @@ function publishConfig(config) {
416
486
  return null;
417
487
  }
418
488
 
419
- const { owner, name } = releasesRepo(config);
420
- if (!owner || !name) {
489
+ const releases = releasesRepo(config);
490
+ if (!releases) {
421
491
  return null;
422
492
  }
423
493
 
424
- return { provider: 'github', owner, repo: name, releaseType: 'release' };
494
+ return { provider: 'github', owner: releases.owner, repo: releases.name, releaseType: 'release' };
495
+ }
496
+
497
+ // The electron-builder hooks that notarize ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)):
498
+ // `afterSign` for the .app, and `artifactBuildCompleted` for each .dmg. The
499
+ // ticket stapled to the app INSIDE an image is not stapled to the image, and a
500
+ // user's Mac assesses the image first. The DMG hook is PER ARTIFACT because
501
+ // electron-builder awaits it before it emits the `artifactCreated` its
502
+ // publisher uploads on; `afterAllArtifactBuild` fires after every upload is
503
+ // queued, so a proof there ships an unstapled image (run 34738410986).
504
+ function notarizeHooks() {
505
+ return {
506
+ afterSign: require.resolve('@omega.js/desktop/hooks/notarize'),
507
+ artifactBuildCompleted: require.resolve('@omega.js/desktop/hooks/notarize-artifacts'),
508
+ };
425
509
  }
426
510
 
427
511
  // Whether the packaged app gets LSUIElement=true in Info.plist. True for
@@ -433,6 +517,41 @@ function shouldInjectLSUIElement(config) {
433
517
  || config?.startup?.openAtLogin?.mode === 'hidden';
434
518
  }
435
519
 
520
+ // A config string with something in it, else ''. Never `undefined` leaking into
521
+ // a rendered YAML value.
522
+ function trimmed(value) {
523
+ return typeof value === 'string' ? value.trim() : '';
524
+ }
525
+
526
+ /**
527
+ * Refuse to write a config whose .deb target cannot build (#872). electron-builder
528
+ * fails on a missing homepage or maintainer at PACKAGE time, one target into the
529
+ * linux run and after the AppImage has uploaded; this says the same thing at
530
+ * config time, naming the omega.json5 key to set.
531
+ *
532
+ * @param {object} builderConfig - The composed electron-builder config.
533
+ */
534
+ function assertLinuxPackageMetadata(builderConfig) {
535
+ const targets = (builderConfig.linux?.target || []).map((entry) => (typeof entry === 'string' ? entry : entry?.target));
536
+ if (!targets.includes('deb')) {
537
+ return;
538
+ }
539
+
540
+ const missing = [];
541
+ if (!builderConfig.extraMetadata?.homepage) {
542
+ missing.push('brand.url (the .deb needs a project homepage)');
543
+ }
544
+ if (!builderConfig.linux?.maintainer) {
545
+ missing.push('brand.contact.email (the .deb needs a package maintainer)');
546
+ }
547
+
548
+ if (missing.length === 0) {
549
+ return;
550
+ }
551
+
552
+ throw new Error(`The linux .deb target cannot be built from this config. Set in config/omega.json5: ${missing.join('; ')}. A brand that genuinely has neither can spell targets.desktop.electronBuilder.linux.maintainer and .extraMetadata.homepage directly.`);
553
+ }
554
+
436
555
  // Exported for tests. deepMerge is @omega.js/config's (cp73c consolidation) —
437
556
  // same contract the local copy had: objects merge per-key, arrays REPLACE
438
557
  // (consumer `mac.target: [...]` fully replaces ours, never concatenates).
@@ -440,6 +559,8 @@ module.exports.baseConfig = baseConfig;
440
559
  module.exports.deepMerge = deepMerge;
441
560
  module.exports.shouldInjectLSUIElement = shouldInjectLSUIElement;
442
561
  module.exports.publishConfig = publishConfig;
562
+ module.exports.notarizeHooks = notarizeHooks;
443
563
  module.exports.expandYear = expandYear;
444
564
  module.exports.resolveCategory = resolveCategory;
445
565
  module.exports.CATEGORY_MAP = CATEGORY_MAP;
566
+ module.exports.assertLinuxPackageMetadata = assertLinuxPackageMetadata;