@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.
- package/README.md +1 -1
- package/dist/assets/css/tokens/_index.scss +1 -1
- package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +4 -1
- package/dist/assets/themes/base/_includes/frontend/sections/footer.html +13 -6
- package/dist/assets/themes/base/_includes/frontend/sections/nav.html +10 -8
- package/dist/assets/themes/base/_includes/global/sections/account.html +3 -1
- package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +12 -8
- package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +10 -6
- package/dist/assets/themes/base/_includes/global/sections/page-header.html +8 -4
- package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +24 -24
- package/dist/assets/themes/base/_layouts/frontend/pages/about.html +10 -10
- package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +35 -35
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +4 -4
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +4 -4
- package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +3 -3
- package/dist/assets/themes/base/_layouts/frontend/pages/download.html +34 -33
- package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +11 -11
- package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/status.html +1 -1
- package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +9 -7
- package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +5 -3
- package/dist/assets/themes/base/_sections/about/letter/section.html +1 -1
- package/dist/assets/themes/base/_sections/about/letter/section.json5 +4 -4
- package/dist/assets/themes/base/_sections/marketing/bento/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +15 -15
- package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +1 -1
- package/dist/assets/themes/base/_sections/marketing/hero/section.html +6 -6
- package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +10 -10
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +2 -2
- package/dist/assets/themes/base/_sections/marketing/stats/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +5 -5
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +1 -1
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +2 -2
- package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +10 -10
- package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +10 -10
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +2 -2
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +4 -4
- package/dist/build.js +69 -28
- package/dist/cli-run.js +20 -13
- package/dist/cli.js +12 -7
- package/dist/commands/build.js +0 -2
- package/dist/commands/deploy.js +116 -44
- package/dist/commands/finalize-release.js +5 -4
- package/dist/commands/launch.js +12 -7
- package/dist/commands/lib/deploy-precheck.js +66 -34
- package/dist/commands/lib/ensure-target.js +71 -11
- package/dist/commands/package.js +0 -1
- package/dist/commands/publish.js +11 -4
- package/dist/commands/release.js +99 -252
- package/dist/commands/runner.js +42 -5
- package/dist/commands/sign-windows.js +46 -15
- package/dist/commands/test.js +4 -3
- package/dist/commands/validate-certs.js +291 -115
- package/dist/config/page-template.html +4 -0
- package/dist/defaults/.github/workflows/build.yml +144 -64
- package/dist/defaults/AGENTS.md +3 -2
- package/dist/defaults/_.gitignore +4 -2
- package/dist/defaults/config/certs/README.md +25 -45
- package/dist/defaults/config/omega.json5 +66 -32
- package/dist/defaults/hooks/deploy/pre.js +10 -0
- package/dist/defaults/src/assets/scss/main.scss +12 -2
- package/dist/defaults/src/integrations/tray/index.js +1 -1
- package/dist/gulp/main.js +13 -25
- package/dist/gulp/tasks/audit.js +18 -6
- package/dist/gulp/tasks/build-config.js +181 -60
- package/dist/gulp/tasks/bundle.js +108 -36
- package/dist/gulp/tasks/release.js +86 -4
- package/dist/gulp/tasks/sass.js +11 -0
- package/dist/hooks/lib/notarize-tools.js +137 -0
- package/dist/hooks/notarize-artifacts.js +57 -0
- package/dist/hooks/notarize.js +59 -8
- package/dist/lib/auth-persistence.js +25 -0
- package/dist/lib/client-bridge.js +11 -8
- package/dist/lib/deep-link.js +15 -8
- package/dist/lib/protocol.js +7 -1
- package/dist/lib/restart-manager/install.js +6 -3
- package/dist/lib/sign-helpers/auto-unlock.js +65 -27
- package/dist/lib/sign-helpers/console-lock.js +34 -0
- package/dist/lib/sign-helpers/exec-with-limit.js +68 -0
- package/dist/lib/sign-helpers/resolve-icons.js +6 -4
- package/dist/lib/tray.js +7 -6
- package/dist/main.js +16 -0
- package/dist/preload.js +8 -0
- package/dist/renderer.js +16 -4
- package/dist/runner/job-started.js +104 -0
- package/dist/test/fixtures/consumer-app/config/omega.json5 +7 -0
- package/dist/test/fixtures/consumer-app/package.json +1 -1
- package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +7 -1
- package/dist/test/harness/main-entry.js +2 -1
- package/dist/test/harness/renderer-preload.js +12 -3
- package/dist/test/runners/boot.js +101 -11
- package/dist/test/runners/electron.js +1 -1
- package/dist/test/suites/boot/consumer-app-boots.test.js +54 -0
- package/dist/test/suites/build/audit.test.js +45 -6
- package/dist/test/suites/build/auth-persistence-resolve.test.js +119 -0
- package/dist/test/suites/build/auto-unlock.test.js +105 -0
- package/dist/test/suites/build/boot-runner-timeout.test.js +253 -0
- package/dist/test/suites/build/brand-scss.test.js +106 -0
- package/dist/test/suites/build/build-config.test.js +146 -36
- package/dist/test/suites/build/build-json-bake.test.js +245 -0
- package/dist/test/suites/build/build-verbs.test.js +48 -15
- package/dist/test/suites/build/build-workflow-jobs.test.js +190 -0
- package/dist/test/suites/build/cli.test.js +32 -10
- package/dist/test/suites/build/config-schema.test.js +4 -4
- package/dist/test/suites/build/console-lock.test.js +51 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +71 -2
- package/dist/test/suites/build/deploy-direct.test.js +241 -0
- package/dist/test/suites/build/deploy-dispatch.test.js +287 -0
- package/dist/test/suites/build/deploy-hook.test.js +169 -0
- package/dist/test/suites/build/ensure-target.test.js +14 -2
- package/dist/test/suites/build/env-delivery.test.js +19 -10
- package/dist/test/suites/build/env-watch.test.js +18 -7
- package/dist/test/suites/build/esm-only-dependency.test.js +127 -0
- package/dist/test/suites/build/exec-with-limit.test.js +53 -0
- package/dist/test/suites/build/finalize-release.test.js +2 -2
- package/dist/test/suites/build/get-config.test.js +120 -8
- package/dist/test/suites/build/github-utils.test.js +12 -6
- package/dist/test/suites/build/license-stamp.test.js +6 -4
- package/dist/test/suites/build/manager.test.js +88 -68
- package/dist/test/suites/build/manifest-deps.test.js +116 -0
- package/dist/test/suites/build/merge-line-files.test.js +27 -4
- package/dist/test/suites/build/notarize-artifacts.test.js +135 -0
- package/dist/test/suites/build/notarize-tools.test.js +38 -0
- package/dist/test/suites/build/notarize.test.js +207 -0
- package/dist/test/suites/build/release-pipeline.test.js +38 -60
- package/dist/test/suites/build/release-skipped-upload.test.js +107 -0
- package/dist/test/suites/build/resolve-icons.test.js +35 -35
- package/dist/test/suites/build/runner-job-guard.test.js +182 -0
- package/dist/test/suites/build/runner.test.js +25 -2
- package/dist/test/suites/build/sentry.test.js +8 -3
- package/dist/test/suites/build/setup-scripts.test.js +3 -0
- package/dist/test/suites/build/sign-windows.test.js +92 -8
- package/dist/test/suites/build/test-stealth.test.js +17 -11
- package/dist/test/suites/build/url-helpers.test.js +37 -17
- package/dist/test/suites/build/validate-certs.test.js +428 -55
- package/dist/test/suites/build/validate-config.test.js +3 -3
- package/dist/test/suites/main/auth-flow.test.js +12 -0
- package/dist/test/suites/main/auth-persistence.test.js +14 -17
- package/dist/test/suites/main/auto-updater.test.js +2 -2
- package/dist/test/suites/main/boot-sequence.test.js +1 -1
- package/dist/test/suites/main/client-bridge.integration.test.js +5 -82
- package/dist/test/suites/main/client-bridge.test.js +19 -6
- package/dist/test/suites/main/deep-link.test.js +54 -0
- package/dist/test/suites/main/startup-paths-and-ua.test.js +1 -1
- package/dist/test/suites/main/url-helpers.test.js +81 -72
- package/dist/test/suites/renderer/cross-context-helpers.test.js +19 -16
- package/dist/utils/build-pipeline.js +10 -10
- package/dist/utils/github.js +12 -51
- package/dist/utils/load-env.js +66 -0
- package/dist/utils/mode-helpers.js +43 -111
- package/dist/utils/platform.js +37 -0
- package/dist/utils/runner-env.js +2 -1
- package/dist/utils/runner-job-guard.js +149 -0
- package/dist/utils/ship-keys.js +52 -0
- package/dist/utils/test-stealth.js +5 -3
- package/dist/utils/url-helpers.js +33 -17
- package/dist/vendor/config/bundle-id.js +53 -0
- package/dist/vendor/config/client-config.js +141 -0
- package/dist/vendor/config/company.js +334 -15
- package/dist/vendor/config/dev-facts.js +48 -0
- package/dist/vendor/config/env-delivery.js +231 -9
- package/dist/vendor/config/env-retired.js +137 -0
- package/dist/vendor/config/env-rules.js +22 -3
- package/dist/vendor/config/env-schema.js +234 -117
- package/dist/vendor/config/env.js +55 -26
- package/dist/vendor/config/environment.js +189 -0
- package/dist/vendor/config/hooks.js +13 -11
- package/dist/vendor/config/index.js +121 -44
- package/dist/vendor/config/load.js +366 -115
- package/dist/vendor/config/merge.js +2 -2
- package/dist/vendor/config/order.js +3 -3
- package/dist/vendor/config/platforms.js +276 -0
- package/dist/vendor/config/repo.js +226 -104
- package/dist/vendor/config/retired-keys.js +232 -27
- package/dist/vendor/config/schema.js +525 -99
- package/dist/vendor/config/site-global.js +63 -53
- package/dist/vendor/config/targets.js +187 -0
- package/dist/vendor/config/validate.js +119 -59
- package/dist/vendor/devkit/argv.js +118 -0
- package/dist/vendor/devkit/attach-log-file.js +21 -13
- package/dist/vendor/devkit/brand-tokens.js +278 -0
- package/dist/vendor/devkit/brand-version.js +264 -0
- package/dist/vendor/devkit/build-json.js +91 -0
- package/dist/vendor/devkit/bundle.js +48 -0
- package/dist/vendor/devkit/certificate-expiry.js +108 -0
- package/dist/vendor/devkit/certs.js +16 -194
- package/dist/vendor/devkit/ci-workflows.js +124 -8
- package/dist/vendor/devkit/cli-router.js +3 -3
- package/dist/vendor/devkit/defaults-engine.js +69 -7
- package/dist/vendor/devkit/deploy-follow.js +297 -0
- package/dist/vendor/devkit/deploy-precheck.js +23 -8
- package/dist/vendor/devkit/deploy-record.js +11 -27
- package/dist/vendor/devkit/deploy-snapshot.js +661 -0
- package/dist/vendor/devkit/deploy.js +445 -75
- package/dist/vendor/devkit/git-auth.js +73 -0
- package/dist/vendor/devkit/git-remote.js +95 -0
- package/dist/vendor/devkit/github-repo.js +290 -0
- package/dist/vendor/devkit/local.js +47 -0
- package/dist/vendor/devkit/merge-line-files.js +23 -16
- package/dist/vendor/devkit/omega-bin.js +18 -3
- package/dist/vendor/devkit/pack-local.js +391 -0
- package/dist/vendor/devkit/preludes/index.js +120 -0
- package/dist/vendor/devkit/preludes/origin-heal.js +156 -0
- package/dist/vendor/devkit/service-account.js +43 -0
- package/dist/vendor/devkit/ship-plan.js +112 -0
- package/dist/vendor/devkit/signing-env.js +180 -0
- package/dist/vendor/devkit/signing-tree.js +92 -0
- package/dist/vendor/devkit/target-seams.js +142 -0
- package/dist/vendor/devkit/target-secrets.js +235 -50
- package/dist/vendor/devkit/test/esm-only-fixture.js +48 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/browser.js +19 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/index.js +20 -0
- package/dist/vendor/devkit/test/fixtures/esm-only-package/package.json +13 -0
- package/dist/vendor/monitoring/env.js +20 -10
- package/dist/vendor/monitoring/main.js +1 -1
- package/dist/vendor/monitoring/preload.js +1 -1
- package/dist/vendor/monitoring/renderer.js +1 -1
- package/docs/analytics.md +1 -1
- package/docs/auto-updater.md +5 -5
- package/docs/boot-sequence.md +1 -1
- package/docs/build-system.md +15 -7
- package/docs/client-bridge.md +9 -7
- package/docs/config-schema.md +4 -4
- package/docs/css.md +8 -2
- package/docs/deep-link.md +12 -4
- package/docs/environment-detection.md +32 -24
- package/docs/hooks.md +3 -1
- package/docs/icons.md +7 -7
- package/docs/index.md +61 -23
- package/docs/installer-options.md +24 -21
- package/docs/logging.md +5 -5
- package/docs/releasing.md +25 -17
- package/docs/runner.md +40 -5
- package/docs/shared/brands.md +12 -6
- package/docs/shared/breaking-changes.md +375 -21
- package/docs/shared/config.md +757 -199
- package/docs/shared/deploys.md +194 -91
- package/docs/shared/icons.md +18 -0
- package/docs/shared/local-dev.md +24 -6
- package/docs/shared/logging.md +9 -6
- package/docs/shared/monitoring.md +27 -13
- package/docs/shared/publishing.md +3 -3
- package/docs/shared/rulings.md +2 -2
- package/docs/shared/testing.md +1 -1
- package/docs/shared/theming.md +26 -1
- package/docs/shared/translation.md +49 -7
- package/docs/shared/updates.md +1 -1
- package/docs/signing.md +59 -33
- package/docs/test-framework.md +10 -5
- package/docs/themes.md +15 -1
- package/package.json +18 -12
- package/bin/omega-desktop +0 -2
- package/dist/commands/push-secrets.js +0 -141
- package/dist/test/suites/build/deliver-certs.test.js +0 -95
- package/dist/test/suites/build/derive-signing-env.test.js +0 -122
- package/dist/test/suites/build/push-secrets.test.js +0 -226
- package/dist/test/suites/build/resolve-signing-cert.test.js +0 -342
- package/dist/utils/deliver-certs.js +0 -69
- package/dist/utils/derive-signing-env.js +0 -56
- package/dist/utils/resolve-signing-cert.js +0 -175
- package/dist/vendor/config/desktop-artifacts.js +0 -110
- package/dist/vendor/config/instances.js +0 -208
- /package/dist/defaults/config/icons/{macos → mac}/dmg.png +0 -0
- /package/dist/defaults/config/icons/{macos → mac}/icon.png +0 -0
- /package/dist/defaults/config/icons/{macos → mac}/tray.png +0 -0
|
@@ -9,10 +9,15 @@
|
|
|
9
9
|
* the ONE copy; each framework binds it with its own target string and nothing
|
|
10
10
|
* else.
|
|
11
11
|
*
|
|
12
|
-
* 1. DERIVE
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* 1. DERIVE: the KEY SET is @omega.js/config'sONE delivery primitive, read
|
|
13
|
+
* off the schema AND the composed values (`publishSecretKeys(target,
|
|
14
|
+
* { values })`): every entry whose `delivery` names the target, plus the
|
|
15
|
+
* `match` family members this brand actually configured
|
|
16
|
+
* ([#876](https://github.com/Omega-JS-Stack/omega/issues/876)) and the
|
|
17
|
+
* keys the schema does not know at all, a consumer's own
|
|
18
|
+
* ([#835](https://github.com/Omega-JS-Stack/omega/issues/835)). Still not
|
|
19
|
+
* "whatever the composed .env holds": a declared key this target does not
|
|
20
|
+
* deliver, and every `machineLocal` one, stay home.
|
|
16
21
|
* 2. COLLECT — the VALUES come from the target's COMPOSED env
|
|
17
22
|
* (`composeTargetEnv`, [#678](https://github.com/Omega-JS-Stack/omega/issues/678)):
|
|
18
23
|
* company ← brand ← target `.env`, the brand-side layers schema-filtered
|
|
@@ -23,7 +28,7 @@
|
|
|
23
28
|
* schema drops them from the delivered set itself.
|
|
24
29
|
* 3. GUARD — secrets belong to the repo whose Actions run the workflow, and
|
|
25
30
|
* the only acceptable proof of which repo that is, is the brand's own
|
|
26
|
-
* config (`repo.
|
|
31
|
+
* config (`repo.org`, the SOURCE repo). An inferred git remote is not proof: a
|
|
27
32
|
* target vendored into a framework/test monorepo, a cloned starter still
|
|
28
33
|
* pointing at the template author, or any fork would publish this brand's
|
|
29
34
|
* `.env` to a stranger's Actions.
|
|
@@ -34,20 +39,80 @@
|
|
|
34
39
|
* a skip — it throws the boundary's instructions (install, `gh auth login`, or
|
|
35
40
|
* `omega deploy --no-secrets`).
|
|
36
41
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
+
* A key the SCHEMA says this target's config requires (`required` /
|
|
43
|
+
* `requiredWhen`, @omega.js/config's env-rules) and the cascade resolves EMPTY
|
|
44
|
+
* stops the publish outright ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)):
|
|
45
|
+
* the run that reads those secrets is the one that signs a release, and half a
|
|
46
|
+
* signing set on a runner is a green build nobody can install. Nothing is
|
|
47
|
+
* pushed on that path, so the fix and the re-run are one step, not a partial
|
|
48
|
+
* state to reason about. A key that is merely absent and NOT required gets one
|
|
49
|
+
* line naming it.
|
|
50
|
+
*
|
|
51
|
+
* There is no per-framework BIND any more
|
|
52
|
+
* ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the four files
|
|
53
|
+
* that passed a target string and a seam were four places to forget a rule, and
|
|
54
|
+
* the standalone `omega push-secrets` verb they backed is gone with them. The
|
|
55
|
+
* seams live HERE, in one table keyed by target type, and every caller (each
|
|
56
|
+
* framework's deploy precheck, the manager's repo walk) makes the same call.
|
|
57
|
+
*
|
|
58
|
+
* desktop `resolveValue` base64-encodes a secret that names a FILE
|
|
59
|
+
* (#682: `CSC_LINK` points at a `.p12` and CI needs the bytes), and
|
|
60
|
+
* `deriveValues` fills the signing paths from the signing tree, by
|
|
61
|
+
* the ONE derivation the desktop env load runs
|
|
62
|
+
* (`@omega.js/devkit/signing-env`), so no operator pastes a path.
|
|
63
|
+
* backend `extraSecrets`: the deploy credential is a service-account FILE
|
|
64
|
+
* the cloud manage service mints (#872), so there is nothing in the
|
|
65
|
+
* cascade to compose it from and it arrives already valued.
|
|
66
|
+
* web, extension plain strings, no seam.
|
|
67
|
+
*
|
|
68
|
+
* `dryRun` ([#895](https://github.com/Omega-JS-Stack/omega/issues/895)) prints
|
|
69
|
+
* the key NAMES that would publish, the derived lines and any refusal, and
|
|
70
|
+
* sends nothing. The refusal still THROWS in a dry run: a plan that cannot be
|
|
71
|
+
* made is loud.
|
|
42
72
|
*/
|
|
43
|
-
const { composeTargetEnv } = require('../config/index.js');
|
|
73
|
+
const { composeTargetEnv, loadConfig } = require('../config/index.js');
|
|
44
74
|
const { publishSecretKeys } = require('../config/env-delivery.js');
|
|
75
|
+
const { checkEnvRules } = require('../config/env-rules.js');
|
|
45
76
|
const { publishActionsSecrets } = require('./actions-secrets.js');
|
|
46
|
-
const { resolveRepo } = require('./deploy.js');
|
|
77
|
+
const { resolveRepo, resolveDeployLane } = require('./deploy.js');
|
|
78
|
+
const { targetSeams } = require('./target-seams.js');
|
|
47
79
|
|
|
48
80
|
/**
|
|
49
|
-
* The
|
|
50
|
-
*
|
|
81
|
+
* The target's COMPOSED env, with the bind's own derivations applied to it.
|
|
82
|
+
*
|
|
83
|
+
* PRODUCTION, named rather than sniffed: these values fuel the runner's release
|
|
84
|
+
* build, and the composer would otherwise default to whatever environment this
|
|
85
|
+
* shell happens to be
|
|
86
|
+
* ([#586](https://github.com/Omega-JS-Stack/omega/issues/586)): the same
|
|
87
|
+
* determinism the "FILES only, never the shell" rule above buys.
|
|
88
|
+
*
|
|
89
|
+
* Everything that reads the cascade reads it through HERE (the required-key
|
|
90
|
+
* check and the collection both), so a derived value is as real as a typed one
|
|
91
|
+
* to each of them ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
|
|
92
|
+
*
|
|
93
|
+
* @param {object} options
|
|
94
|
+
* @param {string} options.targetDir - The target root.
|
|
95
|
+
* @param {string} options.target - Target name.
|
|
96
|
+
* @param {function} [options.deriveValues] - The derive seam (see collectTargetSecrets).
|
|
97
|
+
* @param {object} [options.logger] - `{ log, warn, error }`; each derivation prints one line.
|
|
98
|
+
* @returns {{ values: object, fixes: Object<string, string> }}
|
|
99
|
+
*/
|
|
100
|
+
function composeValues({ targetDir, target, deriveValues, logger }) {
|
|
101
|
+
const { values } = composeTargetEnv({ targetDir, target, environment: 'production' });
|
|
102
|
+
|
|
103
|
+
const { derived = [], fixes = {} } = (deriveValues ? deriveValues(values) : null) || {};
|
|
104
|
+
for (const { key, value } of derived) {
|
|
105
|
+
if (logger) logger.log(`${key} derived from ${value}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { values, fixes };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The publishable secrets for a target: the delivery set the ONE primitive
|
|
113
|
+
* answers for it (schema names, family members, the consumer's own keys),
|
|
114
|
+
* valued from the same composed env the set is read off. An empty value never
|
|
115
|
+
* claims a key.
|
|
51
116
|
*
|
|
52
117
|
* @param {object} options
|
|
53
118
|
* @param {string} options.targetDir - The target root (its .env is the local layer).
|
|
@@ -56,20 +121,32 @@ const { resolveRepo } = require('./deploy.js');
|
|
|
56
121
|
* applied to each collected value before it travels (desktop base64-encodes
|
|
57
122
|
* its file-path signing secrets). A falsy return DROPS the key, exactly as an
|
|
58
123
|
* empty composed value does.
|
|
124
|
+
* @param {function} [options.deriveValues] - `(values) => { derived, fixes }`, the
|
|
125
|
+
* seam applied to the COMPOSED map before anything reads it: a bind may VALUE
|
|
126
|
+
* a key the cascade left empty (desktop derives its signing paths from the
|
|
127
|
+
* dispersed artifacts) and may name, per key it could NOT value, the fix line
|
|
128
|
+
* the refusal should carry. `derived` is `[{ key, value }]` (already written
|
|
129
|
+
* into the map); `fixes` is `key → sentence`.
|
|
59
130
|
* @returns {Object<string, string>} key → value, ready to publish.
|
|
60
131
|
*/
|
|
61
132
|
function collectTargetSecrets(options) {
|
|
62
|
-
const { targetDir, target,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// determinism the "FILES only, never the shell" rule above buys.
|
|
68
|
-
const { values } = composeTargetEnv({ targetDir, target, environment: 'production' });
|
|
133
|
+
const { targetDir, target, logger } = options;
|
|
134
|
+
const seams = targetSeams({ target, targetDir });
|
|
135
|
+
const resolveValue = 'resolveValue' in options ? options.resolveValue : seams.resolveValue;
|
|
136
|
+
const deriveValues = 'deriveValues' in options ? options.deriveValues : seams.deriveValues;
|
|
137
|
+
const { values } = composeValues({ targetDir, target, deriveValues, logger });
|
|
69
138
|
|
|
70
139
|
const secrets = {};
|
|
71
|
-
|
|
72
|
-
|
|
140
|
+
// The key set and the values come from ONE composition (#835, #876): a
|
|
141
|
+
// family member or a consumer's own key exists only in the composed map, so
|
|
142
|
+
// the set has to be read off the same map it is valued from.
|
|
143
|
+
for (const key of publishSecretKeys(target, { values })) {
|
|
144
|
+
if (!values[key]) {
|
|
145
|
+
// Never silence: a key with no value in the cascade is a key CI will not
|
|
146
|
+
// have. Whether that MATTERS is the schema's answer, below.
|
|
147
|
+
if (logger) logger.log(`No value for ${key} in the .env cascade: not published`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
73
150
|
const value = resolveValue ? resolveValue(values[key], key) : values[key];
|
|
74
151
|
if (value) secrets[key] = value;
|
|
75
152
|
}
|
|
@@ -78,10 +155,55 @@ function collectTargetSecrets(options) {
|
|
|
78
155
|
}
|
|
79
156
|
|
|
80
157
|
/**
|
|
81
|
-
* The
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
158
|
+
* The keys this target's own config REQUIRES that the cascade cannot value.
|
|
159
|
+
*
|
|
160
|
+
* The rules are the schema's, evaluated by the ONE checker (#626) against the
|
|
161
|
+
* target's resolved config and its COMPOSED env, narrowed to the keys this
|
|
162
|
+
* publisher would send: a required key CI never reads is not the publish's
|
|
163
|
+
* business, and a rule nobody delivers cannot be fixed by pushing anything.
|
|
164
|
+
*
|
|
165
|
+
* The bind's derivations run FIRST (`deriveValues`), because a key the brand
|
|
166
|
+
* never typed but the dispersed files answer is not missing, and a key the
|
|
167
|
+
* derivation could not value carries that seam's own fix line
|
|
168
|
+
* ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
|
|
169
|
+
*
|
|
170
|
+
* @param {object} options
|
|
171
|
+
* @param {string} options.targetDir - The target root.
|
|
172
|
+
* @param {string} options.target - Target name.
|
|
173
|
+
* @param {function} [options.deriveValues] - The derive seam (see collectTargetSecrets).
|
|
174
|
+
* @returns {Array<{ key: string, rule: string, path: string|null, fix: string|null }>}
|
|
175
|
+
*/
|
|
176
|
+
function missingRequiredSecrets(options) {
|
|
177
|
+
const { targetDir, target } = options;
|
|
178
|
+
const deriveValues = 'deriveValues' in options
|
|
179
|
+
? options.deriveValues
|
|
180
|
+
: targetSeams({ target, targetDir }).deriveValues;
|
|
181
|
+
|
|
182
|
+
let config;
|
|
183
|
+
try {
|
|
184
|
+
// PRODUCTION, the same environment `composeValues` composes for: the rules
|
|
185
|
+
// and the values have to be read off ONE config, or a developer machine's
|
|
186
|
+
// `config/omega.development.json5` decides whether a release publish
|
|
187
|
+
// refuses ([#895](https://github.com/Omega-JS-Stack/omega/issues/895)).
|
|
188
|
+
config = loadConfig(targetDir, target, { environment: 'production' }).config;
|
|
189
|
+
} catch (e) {
|
|
190
|
+
// No loadable config: there is no declaration to owe anything to.
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const { values, fixes } = composeValues({ targetDir, target, deriveValues });
|
|
195
|
+
const delivered = new Set(publishSecretKeys(target, { values }));
|
|
196
|
+
|
|
197
|
+
return checkEnvRules(config, values, { target })
|
|
198
|
+
.filter((violation) => delivered.has(violation.key))
|
|
199
|
+
.map((violation) => ({ ...violation, fix: fixes[violation.key] || null }));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The brand's SOURCE repo as `owner/name`, from the target's resolved config:
|
|
204
|
+
* the one the workflows and their secrets live on, derived as
|
|
205
|
+
* `<brand.id>-omega` under `repo.org` (#883). Null when the config declares
|
|
206
|
+
* nothing usable or doesn't load.
|
|
85
207
|
*
|
|
86
208
|
* @param {object} options
|
|
87
209
|
* @param {string} options.targetDir - The target root.
|
|
@@ -90,13 +212,14 @@ function collectTargetSecrets(options) {
|
|
|
90
212
|
*/
|
|
91
213
|
function declaredBrandRepo(options) {
|
|
92
214
|
try {
|
|
93
|
-
const { loadConfig,
|
|
94
|
-
|
|
215
|
+
const { loadConfig, sourceRepo } = require('../config/index.js');
|
|
216
|
+
// PRODUCTION, like every other read this publish makes (#895): the repo a
|
|
217
|
+
// release's secrets belong to is the one the production config names.
|
|
218
|
+
const { config } = loadConfig(options.targetDir, options.target, { environment: 'production' });
|
|
95
219
|
if (!config) return null;
|
|
96
220
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
return owner && name ? `${owner}/${name}` : null;
|
|
221
|
+
const source = sourceRepo(config);
|
|
222
|
+
return source ? source.slug : null;
|
|
100
223
|
} catch (e) {
|
|
101
224
|
return null;
|
|
102
225
|
}
|
|
@@ -111,16 +234,28 @@ function declaredBrandRepo(options) {
|
|
|
111
234
|
* @param {string} options.target - Target name ('web', 'extension', …).
|
|
112
235
|
* @param {object} options.logger - `{ log, warn, error }`.
|
|
113
236
|
* @param {object} [options.env] - Env map (default: process.env).
|
|
114
|
-
* @param {
|
|
237
|
+
* @param {boolean} [options.dryRun] - Print the plan (key NAMES, the derived
|
|
238
|
+
* lines, any refusal) and send nothing (#895).
|
|
239
|
+
* @param {function} [options.resolveValue] - Value seam override (tests).
|
|
240
|
+
* @param {function} [options.deriveValues] - Derive seam override (tests).
|
|
241
|
+
* @param {Object<string, string>} [options.extraSecrets] - Override for the
|
|
242
|
+
* target's already-valued keys, merged OVER the composed set (tests).
|
|
115
243
|
* @param {function} [options.execFn] - Injectable `gh` exec (tests).
|
|
116
244
|
* @param {function} [options.gitExecFn] - Injectable `git` exec for remote discovery (tests).
|
|
117
|
-
* @returns {{ skipped: string }|{ published: string[], failed: Array<object> }}
|
|
245
|
+
* @returns {{ skipped: string }|{ planned: string[] }|{ published: string[], failed: Array<object> }}
|
|
118
246
|
*/
|
|
119
247
|
function publishTargetSecrets(options) {
|
|
120
248
|
options = options || {};
|
|
121
|
-
const { targetDir, target, logger } = options;
|
|
249
|
+
const { targetDir, target, logger, dryRun } = options;
|
|
122
250
|
const env = options.env || process.env;
|
|
123
251
|
|
|
252
|
+
// The target type's own shape differences, in ONE table (#891). A caller
|
|
253
|
+
// passes a seam only to stub it.
|
|
254
|
+
const seams = targetSeams({ target, targetDir });
|
|
255
|
+
const resolveValue = 'resolveValue' in options ? options.resolveValue : seams.resolveValue;
|
|
256
|
+
const deriveValues = 'deriveValues' in options ? options.deriveValues : seams.deriveValues;
|
|
257
|
+
const extraSecrets = 'extraSecrets' in options ? options.extraSecrets : seams.extraSecrets;
|
|
258
|
+
|
|
124
259
|
// CI runs the generated workflow as its build/publish step — the secrets
|
|
125
260
|
// already exist there and the runner token can't write them.
|
|
126
261
|
if (env.GITHUB_ACTIONS === 'true' || env.CI === 'true') {
|
|
@@ -128,30 +263,80 @@ function publishTargetSecrets(options) {
|
|
|
128
263
|
return { skipped: 'ci' };
|
|
129
264
|
}
|
|
130
265
|
|
|
131
|
-
|
|
266
|
+
// The schema's set, plus the caller's own already-valued keys. An extra has
|
|
267
|
+
// no `.env` home to compose from (backend's deploy credential is a FILE the
|
|
268
|
+
// firebase service mints, #872), so it arrives valued and travels verbatim:
|
|
269
|
+
// `resolveValue` is the COMPOSED half's seam and would only mangle it. An
|
|
270
|
+
// empty extra claims no key, exactly as an empty composed value does.
|
|
271
|
+
// The config's OWN requirements first: a half set is never published (#891)
|
|
272
|
+
const missing = missingRequiredSecrets({ targetDir, target, deriveValues });
|
|
273
|
+
if (missing.length > 0) {
|
|
274
|
+
// ONE line shape per key, whatever answers it: `<KEY> (required by <path>):
|
|
275
|
+
// <the fix>`. A key a bind can DERIVE names the producer that delivers the
|
|
276
|
+
// file (`omega manage --service certificates`, then disperse) instead of
|
|
277
|
+
// asking for a path to paste; everything else names the .env to set.
|
|
278
|
+
const lines = missing.map(({ key, path, fix }) => {
|
|
279
|
+
const detail = fix || `set it in the ${target} target's .env or the brand's, then re-run.`;
|
|
280
|
+
|
|
281
|
+
return ` ${key}${path ? ` (required by ${path})` : ''}: ${detail}`;
|
|
282
|
+
});
|
|
283
|
+
throw new Error(
|
|
284
|
+
`${missing.length} secret(s) this brand's config requires are empty in the .env cascade (company/brand/target). `
|
|
285
|
+
+ `Nothing was published.\n${lines.join('\n')}`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const secrets = { ...collectTargetSecrets({ targetDir, target, resolveValue, deriveValues, logger }) };
|
|
290
|
+
for (const [key, value] of Object.entries(extraSecrets || {})) {
|
|
291
|
+
if (value) secrets[key] = value;
|
|
292
|
+
}
|
|
293
|
+
|
|
132
294
|
const keys = Object.keys(secrets);
|
|
133
295
|
if (!keys.length) {
|
|
134
296
|
logger.warn('Skipping secret publication — no keys composed for this target from the .env cascade (company/brand/target)');
|
|
135
297
|
return { skipped: 'no-secrets' };
|
|
136
298
|
}
|
|
137
299
|
|
|
138
|
-
let repo;
|
|
139
|
-
try {
|
|
140
|
-
const { owner, repo: name } = resolveRepo({ cwd: targetDir, execFn: options.gitExecFn });
|
|
141
|
-
repo = `${owner}/${name}`;
|
|
142
|
-
} catch (e) {
|
|
143
|
-
logger.warn(`Skipping secret publication — no GitHub remote here (${e.message})`);
|
|
144
|
-
return { skipped: 'no-remote' };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
300
|
const declared = declaredBrandRepo({ targetDir, target });
|
|
148
301
|
if (!declared) {
|
|
149
|
-
logger.warn('Skipping secret publication
|
|
302
|
+
logger.warn('Skipping secret publication: this brand names no GitHub repo in config (repo.org). Set it, then re-run `omega deploy`.');
|
|
150
303
|
return { skipped: 'no-declared-repo' };
|
|
151
304
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
305
|
+
|
|
306
|
+
// A NESTED brand (its root is not the toplevel of the git repo it sits in)
|
|
307
|
+
// has a remote that answers the ENCLOSING repo by construction, and its
|
|
308
|
+
// deploy pushes a snapshot to the DECLARED repo regardless
|
|
309
|
+
// ([#872](https://github.com/Omega-JS-Stack/omega/issues/872)), so the
|
|
310
|
+
// mismatch guard would only ever skip a publish that is correct. Every other
|
|
311
|
+
// guard stays: what the guard protects against is a checkout that COULD have
|
|
312
|
+
// been the brand's own and is not.
|
|
313
|
+
const { nested } = resolveDeployLane({ dir: targetDir, execFn: options.gitExecFn });
|
|
314
|
+
const repo = declared;
|
|
315
|
+
|
|
316
|
+
if (!nested) {
|
|
317
|
+
let remote;
|
|
318
|
+
try {
|
|
319
|
+
const { owner, repo: name } = resolveRepo({ cwd: targetDir, execFn: options.gitExecFn });
|
|
320
|
+
remote = `${owner}/${name}`;
|
|
321
|
+
} catch (e) {
|
|
322
|
+
logger.warn(`Skipping secret publication — no GitHub remote here (${e.message})`);
|
|
323
|
+
return { skipped: 'no-remote' };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (declared.toLowerCase() !== remote.toLowerCase()) {
|
|
327
|
+
logger.warn(`Skipping secret publication — the git remote here is ${remote}, but this brand's repo is ${declared}. Run \`omega deploy\` from the brand's own checkout.`);
|
|
328
|
+
return { skipped: 'repo-mismatch' };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// The PLAN, and nothing else (#895): what the composition answered, by NAME.
|
|
333
|
+
// It runs after the refusal and the two guards above on purpose, so a dry run
|
|
334
|
+
// is a real preview of whether the deploy would publish at all: every one of
|
|
335
|
+
// them is a READ, and a plan naming keys the real run would skip is a promise
|
|
336
|
+
// the deploy cannot keep.
|
|
337
|
+
if (dryRun) {
|
|
338
|
+
logger.log(`DRY RUN, would publish ${keys.length} secret(s) for ${target}: ${keys.join(', ')}`);
|
|
339
|
+
return { planned: keys };
|
|
155
340
|
}
|
|
156
341
|
|
|
157
342
|
logger.log(`Publishing ${keys.length} secret(s) to ${repo}: ${keys.join(', ')}`);
|
|
@@ -162,4 +347,4 @@ function publishTargetSecrets(options) {
|
|
|
162
347
|
return result;
|
|
163
348
|
}
|
|
164
349
|
|
|
165
|
-
module.exports = { collectTargetSecrets, declaredBrandRepo, publishTargetSecrets };
|
|
350
|
+
module.exports = { collectTargetSecrets, declaredBrandRepo, missingRequiredSecrets, publishTargetSecrets };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE ESM-only dependency every OMEGA target proves its bundle lane against
|
|
3
|
+
* ([#906](https://github.com/Omega-JS-Stack/omega/issues/906)).
|
|
4
|
+
*
|
|
5
|
+
* `fixtures/esm-only-package/` is a real package (`"type": "module"`, no
|
|
6
|
+
* CommonJS build) with one entry per environment, chosen by export condition:
|
|
7
|
+
* the NODE entry opens a require of its own with
|
|
8
|
+
* `createRequire(import.meta.url)`, the shape that threw in a packaged desktop
|
|
9
|
+
* app, and the BROWSER entry touches no Node built-in. One package, so a target
|
|
10
|
+
* asks for it by its own name and gets the half its bundle is built for.
|
|
11
|
+
*
|
|
12
|
+
* It is INSTALLED rather than pointed at: `installEsmOnlyFixture(projectRoot)`
|
|
13
|
+
* copies it into the project's `node_modules`, which is where a bundler's normal
|
|
14
|
+
* resolution looks and the only shape that is the same in this monorepo and in a
|
|
15
|
+
* consumer running a framework's self-test off its packaged dist (desktop and
|
|
16
|
+
* extension carry the directory there through `omega.vendorAssets`).
|
|
17
|
+
*/
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const jetpack = require('fs-jetpack');
|
|
20
|
+
|
|
21
|
+
// The name a consumer entry imports, and what the fixture's two entries say
|
|
22
|
+
// about themselves. Exported because a test asserting the bundled copy ran must
|
|
23
|
+
// compare against the ONE spelling, not a retyped twin.
|
|
24
|
+
const FIXTURE_NAME = 'esm-only-fixture';
|
|
25
|
+
const FIXTURE_DIR = path.join(__dirname, 'fixtures', 'esm-only-package');
|
|
26
|
+
const NODE_MARKER = 'ESM_ONLY_FIXTURE_NODE';
|
|
27
|
+
const BROWSER_MARKER = 'ESM_ONLY_FIXTURE_BROWSER';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Install the fixture into a project, the way npm installs a dependency.
|
|
31
|
+
* @param {string} projectRoot - the project that will import it
|
|
32
|
+
* @returns {string} where it landed (`<projectRoot>/node_modules/esm-only-fixture`)
|
|
33
|
+
* @throws {Error} when the fixture is not where this module ships it: in a
|
|
34
|
+
* packaged framework that means the vendor step did not carry it, and a test
|
|
35
|
+
* that silently skipped the dependency would prove nothing
|
|
36
|
+
*/
|
|
37
|
+
function installEsmOnlyFixture(projectRoot) {
|
|
38
|
+
if (!jetpack.exists(FIXTURE_DIR)) {
|
|
39
|
+
throw new Error(`[devkit test] the ESM-only fixture is missing from ${FIXTURE_DIR}. A framework running this off its packaged dist needs the omega.vendorAssets entry that carries it`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const destination = path.join(projectRoot, 'node_modules', FIXTURE_NAME);
|
|
43
|
+
jetpack.copy(FIXTURE_DIR, destination, { overwrite: true });
|
|
44
|
+
|
|
45
|
+
return destination;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { installEsmOnlyFixture, FIXTURE_NAME, FIXTURE_DIR, NODE_MARKER, BROWSER_MARKER };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The BROWSER half of the fixture (the `browser` export condition), for the
|
|
2
|
+
// targets whose bundles are browser output: @omega.js/web's esm chunks and
|
|
3
|
+
// service worker, @omega.js/extension's iife bundles. It reaches for no Node
|
|
4
|
+
// built-in and no `import.meta` (esbuild rewrites `import.meta` in iife output
|
|
5
|
+
// too, and warns), because the thing a browser bundle has to prove is the other
|
|
6
|
+
// half of [#906](https://github.com/Omega-JS-Stack/omega/issues/906): an
|
|
7
|
+
// ESM-only package resolves, bundles and RUNS, with a real value on the other
|
|
8
|
+
// side.
|
|
9
|
+
export const flavor = 'browser';
|
|
10
|
+
export const marker = 'ESM_ONLY_FIXTURE_BROWSER';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A value the bundled copy has to compute at RUNTIME, so a test asserting it
|
|
14
|
+
* proves the module evaluated rather than proving its text survived.
|
|
15
|
+
* @returns {string} the fixture's own name for itself
|
|
16
|
+
*/
|
|
17
|
+
export function describeFixture() {
|
|
18
|
+
return `${marker}:${flavor}`;
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The NODE half of the fixture (the `default` export condition): what an
|
|
2
|
+
// ESM-only dependency does the moment it needs a require of its own, which is
|
|
3
|
+
// the shape that broke a packaged desktop app
|
|
4
|
+
// ([#906](https://github.com/Omega-JS-Stack/omega/issues/906)). yargs-parser is
|
|
5
|
+
// the live case: `createRequire(import.meta.url)` with an `import.meta.url` of
|
|
6
|
+
// `undefined` throws ERR_INVALID_ARG_VALUE before a single line of app code
|
|
7
|
+
// runs.
|
|
8
|
+
//
|
|
9
|
+
// `viaRequire` requires a BUILT-IN rather than a file beside this one: a
|
|
10
|
+
// relative require resolves against the caller's own directory, which for a
|
|
11
|
+
// bundled copy is the bundle's, so a relative path would be testing where the
|
|
12
|
+
// output landed instead of whether the require works at all.
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
|
|
17
|
+
export const flavor = 'node';
|
|
18
|
+
export const fileUrl = import.meta.url;
|
|
19
|
+
export const viaRequire = typeof require('node:path').join === 'function';
|
|
20
|
+
export const marker = 'ESM_ONLY_FIXTURE_NODE';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "esm-only-fixture",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "An ESM-only dependency, as every OMEGA target's bundle lane has to be able to carry one (#906)",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"browser": "./browser.js",
|
|
10
|
+
"default": "./index.js"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -8,20 +8,30 @@
|
|
|
8
8
|
* OMEGA_SENTRY_ENABLED=false kill switch — nothing reports, ever
|
|
9
9
|
* OMEGA_TEST_RUNNER a test run never pollutes a live project
|
|
10
10
|
* OMEGA_SENTRY_FORCE=true report from a non-production run (local proving)
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* the ONE environment the DEFAULT production signal, for hosts that
|
|
12
|
+
* pass no runtime one
|
|
13
13
|
*
|
|
14
|
-
* The production signal is the host's to supply
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* process
|
|
14
|
+
* The production signal is the host's to supply, and the DEFAULT is the one
|
|
15
|
+
* environment every OMEGA target answers from
|
|
16
|
+
* ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)):
|
|
17
|
+
* `@omega.js/config/environment`'s `isProduction()`, read off the host the gates
|
|
18
|
+
* were asked for (`process.env.OMEGA_ENVIRONMENT` on Node, the host's baked
|
|
19
|
+
* `config.environment` in a browser-ish context such as a renderer bundle).
|
|
20
|
+
* OMEGA_BUILD_MODE used to be that default, which made it a FIFTH production
|
|
21
|
+
* signal with its own opinion: a build-mode run of a development artifact
|
|
22
|
+
* reported as production, and a packaged production app whose lane did not
|
|
23
|
+
* carry the flag reported as development. @omega.js/backend still passes its
|
|
24
|
+
* own answer in (`Manager.isProduction()`), and a boolean a host supplies
|
|
25
|
+
* always wins.
|
|
19
26
|
*/
|
|
20
27
|
|
|
28
|
+
const { isProduction } = require('../config/environment.js');
|
|
29
|
+
|
|
21
30
|
/**
|
|
22
31
|
* Read the environment gates for core.resolveConfig().
|
|
23
|
-
* @param {object} [host] - the host's own signals
|
|
24
|
-
*
|
|
32
|
+
* @param {object} [host] - the host's own signals, and the context the one
|
|
33
|
+
* environment is read off (a Manager carrying the baked `config`)
|
|
34
|
+
* @param {boolean} [host.isProduction] - overrides the one environment's answer
|
|
25
35
|
* @param {boolean} [host.allowInDev] - the host's own dev opt-in (e.g. @omega.js/backend's `reportErrorsInDev`)
|
|
26
36
|
* @returns {{ killed: boolean, killedReason: string|null, isProduction: boolean, allowInDev: boolean }}
|
|
27
37
|
*/
|
|
@@ -41,7 +51,7 @@ function readGates(host) {
|
|
|
41
51
|
killedReason,
|
|
42
52
|
isProduction: typeof host.isProduction === 'boolean'
|
|
43
53
|
? host.isProduction
|
|
44
|
-
:
|
|
54
|
+
: isProduction.call(host),
|
|
45
55
|
allowInDev: !!host.allowInDev || env.OMEGA_SENTRY_FORCE === 'true',
|
|
46
56
|
};
|
|
47
57
|
}
|
|
@@ -30,7 +30,7 @@ const main = {
|
|
|
30
30
|
initialize(host) {
|
|
31
31
|
main._initialized = true;
|
|
32
32
|
|
|
33
|
-
const { shouldEnable, options, reason } = resolveConfig(host && host.config && host.config.monitoring, readGates());
|
|
33
|
+
const { shouldEnable, options, reason } = resolveConfig(host && host.config && host.config.monitoring, readGates(host));
|
|
34
34
|
main._options = options;
|
|
35
35
|
|
|
36
36
|
if (!shouldEnable) {
|
|
@@ -21,7 +21,7 @@ const preload = {
|
|
|
21
21
|
* @param {object} host - the desktop Manager: { config }
|
|
22
22
|
*/
|
|
23
23
|
initialize(host) {
|
|
24
|
-
const { shouldEnable, reason } = resolveConfig(host && host.config && host.config.monitoring, readGates());
|
|
24
|
+
const { shouldEnable, reason } = resolveConfig(host && host.config && host.config.monitoring, readGates(host));
|
|
25
25
|
preload._enabled = shouldEnable;
|
|
26
26
|
if (!shouldEnable) logger.log(`disabled — ${reason}`);
|
|
27
27
|
},
|
|
@@ -26,7 +26,7 @@ const renderer = {
|
|
|
26
26
|
initialize(host) {
|
|
27
27
|
renderer._initialized = true;
|
|
28
28
|
|
|
29
|
-
const { shouldEnable, options, reason } = resolveConfig(host && host.config && host.config.monitoring, readGates());
|
|
29
|
+
const { shouldEnable, options, reason } = resolveConfig(host && host.config && host.config.monitoring, readGates(host));
|
|
30
30
|
renderer._options = options;
|
|
31
31
|
|
|
32
32
|
if (!shouldEnable) {
|
package/docs/analytics.md
CHANGED
|
@@ -46,7 +46,7 @@ Mint the secret in GA4 Admin → Data Streams → your stream → **Measurement
|
|
|
46
46
|
|
|
47
47
|
### Production builds
|
|
48
48
|
|
|
49
|
-
The `bundle` task's esbuild `define` bakes `process.env.GOOGLE_ANALYTICS_SECRET` into the bundled main process at build time, so packaged apps don't need `.env` at runtime. The build runs with the secret set (CI does this via the GitHub Actions secret
|
|
49
|
+
The `bundle` task's esbuild `define` bakes `process.env.GOOGLE_ANALYTICS_SECRET` into the bundled main process at build time, so packaged apps don't need `.env` at runtime. The build runs with the secret set (CI does this via the GitHub Actions secret `omega deploy`'s precheck pushes).
|
|
50
50
|
|
|
51
51
|
## API
|
|
52
52
|
|
package/docs/auto-updater.md
CHANGED
|
@@ -110,13 +110,13 @@ Long enough that an actively-used app won't surprise-quit mid-task. Short enough
|
|
|
110
110
|
|
|
111
111
|
### Test mode behavior
|
|
112
112
|
|
|
113
|
-
When `manager.isTesting() === true` (
|
|
113
|
+
When `manager.isTesting() === true` (the one input: `OMEGA_ENVIRONMENT=testing`), the auto-updater swaps in test-friendly defaults so a real download → idle wait → install can complete in seconds instead of minutes:
|
|
114
114
|
|
|
115
115
|
- **Idle threshold**: `IDLE_INSTALL_THRESHOLD_MS_TESTING = 3000ms` (3 sec) instead of 15 min.
|
|
116
116
|
- **Both timers**: `IDLE_TICK_MS_TESTING = 500ms` replaces `feedCheckIntervalMs` and `idleEvalIntervalMs`.
|
|
117
117
|
- **`_promptToInstall` short-circuits** before invoking `dialog.showMessageBox`. The native dialog is modal + blocking + would pop a window the test process can't dismiss programmatically. In test mode the prompt logs `[testing] _promptToInstall(...) — skipped native dialog.` and returns. Tests that want to assert prompt behavior override `_promptToInstall` per-test (see `auto-updater.test.js`).
|
|
118
118
|
|
|
119
|
-
This lets the framework's own integration tests drive the full sequence (`OMEGA_DEV_UPDATE=available` → state machine → 500ms tick → 3s idle threshold elapses → stubbed `installNow` fires) in ~5s. Consumers running their own tests should
|
|
119
|
+
This lets the framework's own integration tests drive the full sequence (`OMEGA_DEV_UPDATE=available` → state machine → 500ms tick → 3s idle threshold elapses → stubbed `installNow` fires) in ~5s. Consumers running their own tests should name `OMEGA_ENVIRONMENT=testing` to inherit the same defaults.
|
|
120
120
|
|
|
121
121
|
## Menu integration
|
|
122
122
|
|
|
@@ -224,8 +224,8 @@ The submenu is dev-only (same gate as `view/developer/toggle-devtools`) and is a
|
|
|
224
224
|
|
|
225
225
|
```
|
|
226
226
|
provider: github
|
|
227
|
-
owner: <
|
|
228
|
-
repo:
|
|
227
|
+
owner: <repo.org>
|
|
228
|
+
repo: `${brand.id}-releases`
|
|
229
229
|
releaseType: release
|
|
230
230
|
```
|
|
231
231
|
|
|
@@ -233,7 +233,7 @@ So your private app repo and your public release repo are completely decoupled
|
|
|
233
233
|
|
|
234
234
|
## Failure modes
|
|
235
235
|
|
|
236
|
-
- **Update repo isn't public** → `electron-updater` gets 404 or 401 against a private repo. Fix: ensure the releases repo (`<
|
|
236
|
+
- **Update repo isn't public** → `electron-updater` gets 404 or 401 against a private repo. Fix: ensure the releases repo (`<repo.org>/<brand.id>-releases`) is public; `omega deploy`'s precheck creates it that way.
|
|
237
237
|
- **Token rotates / expires for `releases` repo** — `electron-updater` doesn't authenticate downloads (anonymous public reads). So tokens don't apply on the consumer side.
|
|
238
238
|
- **`app-update.yml` missing in the packaged app** → look at the build log for `electron-builder`'s "creating updates yml" line. If it's skipped, your `publish` block didn't materialize correctly into `dist/electron-builder.yml`.
|
|
239
239
|
- **`error` status with code `ERR_UPDATER_CHANNEL_FILE_NOT_FOUND`** → there's no `latest-mac.yml` (or `latest.yml` / `latest-linux.yml`) at the configured channel. Run a release first.
|
package/docs/boot-sequence.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## Order
|
|
6
6
|
|
|
7
7
|
1. **`startup.applyEarly()`** — first thing, before `whenReady`. Calls `app.dock.hide()` for `mode: 'hidden'` (zero-bounce production via `LSUIElement` baked at build time).
|
|
8
|
-
1b. **userData path isolation** — appends an environment suffix to `app.getPath('userData')` so each environment's session data, logs, and `electron-store` files stay separate on the same machine: production untouched, development gets ` (Development)`, testing (`
|
|
8
|
+
1b. **userData path isolation** — appends an environment suffix to `app.getPath('userData')` so each environment's session data, logs, and `electron-store` files stay separate on the same machine: production untouched, development gets ` (Development)`, testing (`OMEGA_ENVIRONMENT=testing`) gets ` (Testing)`. The testing dir is **wiped at boot** so every test run starts from a clean slate (post-run state stays on disk for inspection until the next run; set `OMEGA_TEST_KEEP_USERDATA=1` to skip the wipe). **Must run before `storage.initialize()`** (which constructs `electron-store` against the path).
|
|
9
9
|
1c. **Global user-agent fallback** — sets `app.userAgentFallback` to a branded template via `node-powertools.template`. Default per-platform templates: `Mozilla/5.0 (... <platform-specific> ...) AppleWebKit/537.36 (KHTML, like Gecko) {brand.name}/{app.version} Chrome/{chrome} Safari/537.36`. Merge tags resolve from `{ brand: { name, id }, app: { version }, chrome, electron, node, platform, arch }`. Every BrowserWindow load + electron-updater fetch + node-fetch via the renderer carries the branded UA. Consumers can override post-init by re-setting `app.userAgentFallback` from their main.js.
|
|
10
10
|
2. **`app.on('before-quit')`** wired — sets `manager._isQuitting = true` so any quit path (Cmd+Q, role:'quit' menu, programmatic `app.quit()`, OS shutdown) bypasses the window-manager's hide-on-close trap.
|
|
11
11
|
3. **`ipc`** — typed channel bus online before any feature can register handlers.
|