@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
package/docs/signing.md
CHANGED
|
@@ -5,52 +5,67 @@ Cross-platform code signing reference. Covers macOS (sign + notarize), Windows (
|
|
|
5
5
|
## tl;dr
|
|
6
6
|
|
|
7
7
|
- **macOS production**: Developer ID Application cert + notarization API key. Files in `config/certs/`. Env vars point at them.
|
|
8
|
-
- **Windows production**: EV USB token (self-hosted runner now) or cloud signing (future, pluggable via `platforms.
|
|
8
|
+
- **Windows production**: EV USB token (self-hosted runner now) or cloud signing (future, pluggable via `platforms.windows.signing.strategy`).
|
|
9
9
|
- **Linux**: No signing for AppImage/deb. Snap/Flatpak have their own pipelines.
|
|
10
10
|
|
|
11
11
|
## Where files live
|
|
12
12
|
|
|
13
|
+
Signing material lives in the SIGNING TREE and is read there, never copied into a target
|
|
14
|
+
([#891](https://github.com/Omega-JS-Stack/omega/issues/891)):
|
|
15
|
+
|
|
13
16
|
```
|
|
17
|
+
<company brand>/company/.omega/certificates/apple/ # read FIRST when the brand names a company
|
|
18
|
+
<your brand>/.omega/certificates/apple/ # read second
|
|
19
|
+
certificates/DEVELOPER_ID_APPLICATION_G2.p12 # what CSC_LINK resolves to
|
|
20
|
+
certificates/DEVELOPER_ID_INSTALLER_G2.p12 # only for .pkg builds (rare)
|
|
21
|
+
AuthKey_XXXXXXXXXX.p8 # what APPLE_API_KEY resolves to
|
|
22
|
+
|
|
14
23
|
<your-app>/
|
|
15
24
|
build/
|
|
16
|
-
entitlements.mac.plist # universal
|
|
25
|
+
entitlements.mac.plist # universal, already in @omega.js/desktop defaults
|
|
17
26
|
icon.icns / icon.ico / icon.png # per-brand
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.env # gitignored — env vars referencing config/certs/*
|
|
27
|
+
config/certs/ # gitignored, and EMPTY on a developer machine:
|
|
28
|
+
# only a RUNNER writes here, decoding the pushed secrets
|
|
29
|
+
README.md # the ONE tracked file here (#913): it says the above
|
|
30
|
+
.env # gitignored, and it carries no signing PATH: only
|
|
31
|
+
# CSC_KEY_PASSWORD and the APPLE_* three
|
|
24
32
|
```
|
|
25
33
|
|
|
34
|
+
There is no provisioning profile: Developer ID is direct distribution, signed and notarized,
|
|
35
|
+
and needs none.
|
|
36
|
+
|
|
26
37
|
## Where the macOS certificate comes from (the lookup order)
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
([src/utils/
|
|
30
|
-
|
|
39
|
+
Every desktop boot (the CLI and gulp alike) derives the signing paths ONCE, at the env load
|
|
40
|
+
([src/utils/load-env.js](../src/utils/load-env.js), over `@omega.js/devkit/signing-env`), so
|
|
41
|
+
the build, `omega validate-certs` and the deploy precheck's secret publish all read the same
|
|
42
|
+
answer. `CSC_LINK` resolves in ONE order:
|
|
31
43
|
|
|
32
44
|
1. **`CSC_LINK`** in the environment — an explicit answer always wins.
|
|
33
|
-
2. **The
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
2. **The COMPANY's tree**, when the brand names a company and that company is on this
|
|
46
|
+
machine: a brand with a company reuses the company tree first, always
|
|
47
|
+
([#677](https://github.com/Omega-JS-Stack/omega/issues/677)). A brand is NEVER physically
|
|
48
|
+
inside its company folder: membership is its `company: { id }` key, resolved through
|
|
49
|
+
`@omega.js/config`'s `resolveCompany` (the ecosystem's one company rule), and the
|
|
50
|
+
certificate is one more relative path through that resolver's `file()`:
|
|
51
|
+
`<company>/company/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12`.
|
|
52
|
+
3. **The brand's own signing tree**:
|
|
53
|
+
`<brandRoot>/.omega/certificates/apple/certificates/DEVELOPER_ID_APPLICATION_G2.p12`.
|
|
54
|
+
`@omega.js/manager`'s certificates service produces the tree
|
|
55
|
+
([the manager guide](../../../docs/manager/index.md)).
|
|
42
56
|
4. **The macOS Keychain** — electron-builder's own identity auto-discovery.
|
|
43
57
|
|
|
58
|
+
`APPLE_API_KEY` follows the same order for `AuthKey_<APPLE_API_KEY_ID>.p8` (an unset id means
|
|
59
|
+
there is no filename to look for). Both derived values are ABSOLUTE paths into the tree.
|
|
60
|
+
|
|
44
61
|
**Keychain stays the default.** A project with no signing tree resolves to it exactly as
|
|
45
62
|
before. Two further conditions keep a build on the Keychain rather than handing
|
|
46
63
|
electron-builder a file it cannot use: no `CSC_KEY_PASSWORD` in the `.env` cascade, or a
|
|
47
64
|
password that does not OPEN the `.p12` (verified with `openssl pkcs12 -legacy`, retried
|
|
48
65
|
without the flag for LibreSSL — stock macOS openssl — which reads legacy containers but
|
|
49
66
|
rejects the flag; a missing openssl is named as the reason rather than blamed on the
|
|
50
|
-
password).
|
|
51
|
-
|
|
52
|
-
The brand-root walk stops **below the home directory**: `~/.omega` is a real personal
|
|
53
|
-
overlay on developer machines, so a tree at or above `~` is never a signing tree.
|
|
67
|
+
password). Either case prints one line naming the file and the reason, and leaves the key
|
|
68
|
+
unset rather than failing the build silently.
|
|
54
69
|
|
|
55
70
|
The password itself never lives in config: `CSC_KEY_PASSWORD` comes from the `.env`
|
|
56
71
|
cascade (the manager's certificates service writes it to the signing root's `.env`),
|
|
@@ -110,11 +125,13 @@ npx omega validate-certs
|
|
|
110
125
|
```
|
|
111
126
|
|
|
112
127
|
This checks:
|
|
113
|
-
- macOS Keychain has a Developer ID Application identity
|
|
114
128
|
- `CSC_LINK` exists and is readable
|
|
129
|
+
- macOS Keychain has a Developer ID Application identity, and ONLY when `CSC_LINK` names no certificate: electron-builder imports a `CSC_LINK` `.p12` into its own temporary keychain, so the login keychain says nothing about that build
|
|
115
130
|
- API key file exists and `APPLE_API_KEY_ID` + `APPLE_API_ISSUER` are set
|
|
116
131
|
- Team ID matches the cert
|
|
117
132
|
|
|
133
|
+
The electron-builder floor is 26.16.1 (the package's peer range, installed by ensure-target for any consumer below it): every earlier build hands the `.p12` password to `security set-key-partition-list`, which wants the temporary keychain's own password, and the macos-26 runner image rejects the mismatch (electron-userland/electron-builder#10066, [#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
|
|
134
|
+
|
|
118
135
|
### 5. Build + notarize
|
|
119
136
|
|
|
120
137
|
```bash
|
|
@@ -141,8 +158,15 @@ Buy an EV code-signing cert from Sectigo, DigiCert, SSL.com, etc. Get a physical
|
|
|
141
158
|
|
|
142
159
|
Setting the box up is [`docs/runner.md`](runner.md) — the runner home, the Startup-folder auto-start, the commands, and the Session 0 / Session 1 rule that makes the whole thing work.
|
|
143
160
|
|
|
161
|
+
**A workflow that targets this runner fires only on dispatch**, and the box enforces it ([#875](https://github.com/Omega-JS-Stack/omega/issues/875)). Whoever can start a job on the signing box can sign binaries with your token, so two guards stand between a stray trigger and `signtool`:
|
|
162
|
+
|
|
163
|
+
- The generated `windows-sign` job runs only on `workflow_dispatch`, the one trigger the file carries (#923). A `push` or `pull_request` trigger added to `build.yml` later SKIPS the sign job instead of signing what it built.
|
|
164
|
+
- The runner itself refuses the job before its first step. `npx omega runner install` writes `job-started.js` beside the runner and points every registration's `ACTIONS_RUNNER_HOOK_JOB_STARTED` at it; the hook exits nonzero, failing the job with one line, unless the event is a dispatch, `GITHUB_REPOSITORY` is on `allowed-repos.txt` and `GITHUB_ACTOR` is on `allowed-actors.txt`. Both lists live on the box and are yours to edit ([`docs/runner.md`](runner.md) § One-time setup).
|
|
165
|
+
|
|
144
166
|
`npx omega sign-windows` is the strategy-aware command that drives `signtool` (or the cloud provider CLI).
|
|
145
167
|
|
|
168
|
+
Every signed artifact is verified before the job moves on, and the log says so in the mac lane's shape: `Signed and verified: signtool verify /pa accepts <file>.` mirrors macOS's `Stapled and verified: ...`, so a deploy log proves the Windows signature was checked rather than only claimed ([#918](https://github.com/Omega-JS-Stack/omega/issues/918)). A failing verify aborts the job.
|
|
169
|
+
|
|
146
170
|
### Cloud signing (future migration)
|
|
147
171
|
|
|
148
172
|
Once the framework's cloud strategy is finalized, set the provider in `config/omega.json5`:
|
|
@@ -163,22 +187,25 @@ If no signing runner is available, CI uploads the unsigned `.exe` and a develope
|
|
|
163
187
|
|
|
164
188
|
## Pushing secrets to GitHub Actions
|
|
165
189
|
|
|
166
|
-
|
|
190
|
+
`omega deploy`'s precheck pushes the target's **composed env** to the repo's GitHub Actions secrets over the same shared `gh` transport the other three frameworks use (`gh secret set`, which seals each value with the repo's public key locally): values travel on **stdin**, never in argv, and are never logged. For env vars whose value is a path to a local file (`.p12`, `.p8`, etc.), the secret value pushed is the **base64-encoded file contents**: the workflow decodes back to a temp file at job start.
|
|
191
|
+
|
|
192
|
+
There is no standalone `omega push-secrets` verb ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the push happens where it matters, and the manage walk's `repo` service runs the same function when you want a brand brought current without deploying.
|
|
167
193
|
|
|
168
194
|
```bash
|
|
169
|
-
# Make sure the brand root's .env holds
|
|
170
|
-
npx omega
|
|
195
|
+
# Make sure the brand root's .env holds CSC_KEY_PASSWORD + the APPLE_* three, and `gh auth login` has run
|
|
196
|
+
npx omega deploy
|
|
171
197
|
|
|
172
|
-
#
|
|
173
|
-
npx omega
|
|
198
|
+
# See exactly what it would publish, and publish nothing
|
|
199
|
+
npx omega deploy --dry-run
|
|
174
200
|
```
|
|
175
201
|
|
|
176
202
|
Behavior:
|
|
177
203
|
- The source is `composeTargetEnv({ targetDir, target: 'desktop' })`: company `.env` ← brand `.env` ← target `.env`, the brand-side layers filtered by the env schema to the keys the desktop target reads. The brand root's `.env` is the one file you keep; a target `.env` is an optional per-key override.
|
|
178
204
|
- Files only — the shell is never a source — and an empty value never claims a key, so unset keys simply don't publish.
|
|
179
205
|
- Auto-detects "is this a path?" — relative or absolute paths ending in `.p12`/`.pem`/`.cer`/`.p8`/`.provisionprofile`/`.crt`/`.key`/`.json` that exist on disk (target root first, brand root second) get base64-encoded.
|
|
180
|
-
- Publishes to the
|
|
206
|
+
- Publishes to the brand's SOURCE repo (`repo.org` + `brand.id` → `<brand.id>-omega`), and REFUSES unless the checkout's own remote IS that repo: a fork, a template clone or a vendored target never arms a stranger's Actions with your certificates.
|
|
181
207
|
- No PAT: the credential is `gh`'s auth session. A missing or signed-out `gh` fails loudly with install/auth instructions; a CI run, an empty cascade, a remote-less checkout or a repo mismatch skips loudly.
|
|
208
|
+
- `CSC_LINK` and `APPLE_API_KEY` are DERIVED, never typed ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the env load resolved them from the signing tree, so the publish sends those files' bytes. A typed value still wins, and a key the config REQUIRES that neither the tree nor the `.env` answers STOPS the deploy (the step is `fatal`), naming both tier paths it looked in and `omega manage --service certificates`, publishing nothing.
|
|
182
209
|
- Logs key NAMES only; never a value.
|
|
183
210
|
|
|
184
211
|
The corresponding decode step in CI looks like:
|
|
@@ -211,7 +238,6 @@ GitHub Actions secrets you'll need (per repo):
|
|
|
211
238
|
| Secret | Purpose |
|
|
212
239
|
|---|---|
|
|
213
240
|
| `GH_TOKEN` | Cross-repo publish, secret rotation |
|
|
214
|
-
| `OMEGA_ADMIN_KEY` | Privileged backend API calls |
|
|
215
241
|
| `CSC_LINK` | base64-encoded `.p12` (workflow decodes to file) |
|
|
216
242
|
| `CSC_KEY_PASSWORD` | `.p12` password |
|
|
217
243
|
| `APPLE_API_KEY` | base64-encoded `.p8` |
|
|
@@ -251,7 +277,7 @@ The workflow base64-decodes secrets into temp files inside `config/certs/` at jo
|
|
|
251
277
|
|
|
252
278
|
### Windows: "SignTool Error: No certificates were found"
|
|
253
279
|
- Token unplugged or middleware not running.
|
|
254
|
-
- For cloud: verify `platforms.
|
|
280
|
+
- For cloud: verify `platforms.windows.signing.cloud.provider` in `config/omega.json5` matches the provider whose creds are in `.env`.
|
|
255
281
|
|
|
256
282
|
## What lives in `build/`
|
|
257
283
|
|
package/docs/test-framework.md
CHANGED
|
@@ -45,6 +45,7 @@ npx omega test --filter="storage" # only suites/tests whose name contains
|
|
|
45
45
|
npx omega test --extended # opt into extended mode — real external APIs (also: TEST_EXTENDED_MODE=true)
|
|
46
46
|
npx omega test --reporter=json # pretty output + machine-readable {"event":"summary",...} line
|
|
47
47
|
OMEGA_TEST_DEBUG=1 npx omega test # see Electron stderr (otherwise drained silently)
|
|
48
|
+
OMEGA_TEST_BOOT_TIMEOUT_MS=120000 npx omega test # the boot lane's budget (default 60s)
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
In @omega.js/desktop itself, `npm test` runs the framework's own suite — the self-test context flips the no-target default.
|
|
@@ -104,13 +105,13 @@ A target that names a path and matches NO file is a hard error: the run prints `
|
|
|
104
105
|
|
|
105
106
|
### Extended vs normal mode
|
|
106
107
|
|
|
107
|
-
Suites that hit a live backend (
|
|
108
|
+
Suites that hit a live backend (a real GitHub API call, a store publish, etc.) are gated behind **extended mode**: `npx omega test --extended` or `TEST_EXTENDED_MODE=true`. `TEST_EXTENDED_MODE` is the **shared, unprefixed env var across @omega.js/backend, @omega.js/extension, UJM, and @omega.js/desktop** (cross-framework parity): once set on `process.env` it propagates to every spawned test environment (the Electron main/renderer/boot children, the gulp boot build) automatically via `{ ...process.env }`. These external calls are **skipped in-source, NOT mocked**: the suite short-circuits / `ctx.skip()`s when `TEST_EXTENDED_MODE` is unset; with it set, it calls the real service. Default is to skip them so `npx omega test` is fast + green offline, and a warning prints when extended mode is on. The CI workflow runs normal mode by default; add a separate workflow that sets `TEST_EXTENDED_MODE: 'true'` for extended coverage.
|
|
108
109
|
|
|
109
110
|
Anything an extended suite creates externally must be torn down in its `cleanup(ctx)` — the harness only resets local Electron state between runs, never external systems.
|
|
110
111
|
|
|
111
|
-
### `
|
|
112
|
+
### `OMEGA_ENVIRONMENT=testing`, the one input a test run names
|
|
112
113
|
|
|
113
|
-
Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`)
|
|
114
|
+
Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`) spawn their child with `OMEGA_ENVIRONMENT=testing`, the ONE environment input ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and nothing writes over an explicit one: the word a build baked into the artifact is the fallback for a packaged app, never an override ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)). That powers `manager.isTesting()` (and the `Manager.isTesting()` static), the cross-context helper everything in @omega.js/desktop checks when it needs to behave differently in tests:
|
|
114
115
|
|
|
115
116
|
- `auto-updater` flips its idle threshold from 15min → 3s and its periodic tick from 60s → 500ms, AND short-circuits the native install-prompt dialog (so tests don't pop modal windows).
|
|
116
117
|
- **Every BrowserWindow surfaces stealth** — named windows via `window-manager._surface()` AND raw `new BrowserWindow()` ones (e.g. a consumer's automation popup) via a global `browser-window-created` hook registered in `main.js` step 1a-ii. The shared recipe lives in `src/utils/stealth-window.js`: shown INACTIVE (keyboard focus never leaves your editor), opacity 0, click-through (`setIgnoreMouseEvents`), and raw windows get `show()` rerouted to `showInactive()` + `focus()` no-op'd — a test run never interrupts you, and a stray real click physically can't land in the app, while synthetic test input (`executeJavaScript`, `sendInputEvent`, CDP) is unaffected. **`webContents.focus()` is suppressed separately** (a global `web-contents-created` hook in the same main.js block): it bypasses the window-level patches — it's a different object whose `focus()` reaches the native window directly and makes the invisible window KEY, grabbing the keyboard mid-typing even under the accessory policy (which only prevents *launch* activation, not key-window steals). Consumers call it legitimately (e.g. address-bar focus on tab select), so it's no-op'd per-contents under the same predicate. Set **`OMEGA_TEST_SHOW=1`** to surface windows normally and watch a run live (the predicate is evaluated per window, so flipping it mid-run works). Deliberately NOT `hide()`/`minimize()`: occluded windows get throttled by Chromium (`requestAnimationFrame` pauses, `document.visibilityState` flips to `hidden`) — tests would exercise a DIFFERENT runtime, whereas an opacity-0 shown-inactive window renders and behaves identically to a visible one. See [windows.md](windows.md).
|
|
@@ -119,9 +120,9 @@ Both @omega.js/desktop test runners (`runners/electron.js`, `runners/boot.js`) s
|
|
|
119
120
|
- **The main-layer harness exposes a CDP endpoint.** `test/harness/main-entry.js` appends `--remote-debugging-port=0` at require time (loopback-only, OS-assigned port) and publishes the resolved port as **`process.env.OMEGA_CDP_PORT`** before suites run (read from Chromium's `DevToolsActivePort` file; any value inherited from your shell is overwritten — that one points at your dev app, not the harness). Consumer suites can drive real browser automation against the harness Electron itself, e.g. `playwright-core`'s `connectOverCDP('http://127.0.0.1:' + process.env.OMEGA_CDP_PORT)`. Covered by `suites/main/harness-cdp.test.js`.
|
|
120
121
|
- Other lib code can branch on `manager.isTesting()` to suppress dock bounce, login-item changes, OS protocol-handler registration, etc.
|
|
121
122
|
|
|
122
|
-
Consumers writing their own tests
|
|
123
|
+
Consumers writing their own tests name the same input in their own runner, for example in `package.json`:
|
|
123
124
|
```json
|
|
124
|
-
"test": "
|
|
125
|
+
"test": "OMEGA_ENVIRONMENT=testing vitest"
|
|
125
126
|
```
|
|
126
127
|
Then in your code, gate test-only behavior on `manager.isTesting()` instead of inventing yet another env var.
|
|
127
128
|
|
|
@@ -295,6 +296,10 @@ All test output is also teed (ANSI-stripped) to `<projectRoot>/logs/test.log`, t
|
|
|
295
296
|
- `ELECTRON_RUN_AS_NODE` is stripped from the spawn env (would otherwise make Electron behave as Node and break the harness).
|
|
296
297
|
- **Consumer-scheme containment.** The consumer's `main.js` — where the real `protocol.handle('<brand.id>', …)` lives — never runs in this harness, so `brand://` URLs loaded by main-layer suites would be UNHANDLED. Chromium treats a load on an unhandled scheme as an *external protocol* and hands it to the OS — and if an installed copy of the app owns the scheme (e.g. `/Applications/Brand.app` on macOS, registered via its `Info.plist`), Launch Services launches the installed production app mid-test-run. The harness contains this two ways after Manager boots: (1) it registers a stub handler for the consumer's brand scheme (read from `<cwd>/config/omega.json5`) on the default session, so `brand://` loads commit in-process as a blank page; (2) it denies the `openExternal` permission on the default session and every session created after it (partitions included), so no navigation can hand ANY scheme to the OS during a test run. Suites that need real page content for `brand://` URLs belong in the boot layer, where the real app (and its real protocol handler) runs. A main-layer suite that genuinely needs its OWN handler for the brand scheme must call `protocol.unhandle(brand.id)` first — the harness stub holds the scheme on the default session.
|
|
297
298
|
|
|
299
|
+
## A test run never touches the OS keychain, and a blocked boot reports
|
|
300
|
+
|
|
301
|
+
The harness resolves auth persistence to the **`none`** strategy on every layer, main and boot alike, before any config is read: a brand that declares `omega.authPersistence: 'safeStorage'` still runs its tests with Firebase auth in memory, because the lanes never sign a real user in. That is a hard rule, not a nicety. The boot lane spawns the raw, unsigned `Electron` binary, which carries no keychain ACL, so one `safeStorage` read parks the entire run behind a macOS SecurityAgent prompt ([#907](https://github.com/Omega-JS-Stack/omega/issues/907)). ONE signal answers "is this a test run" on both layers, `manager.isTesting()`: the boot lane boots the consumer's PRODUCTION artifact, and the word baked into it no longer writes over the `OMEGA_ENVIRONMENT=testing` the runner spawned it with ([#925](https://github.com/Omega-JS-Stack/omega/issues/925)), so the booted app reports the lane it is running in. And since a boot can block long before `main.js` requires the harness (where the per-test timeout lives), the boot runner carries its own budget, measured as SILENCE: every harness line rearms it, so a suite that legitimately runs longer keeps going, and a boot that produces no harness output for 60s (`OMEGA_TEST_BOOT_TIMEOUT_MS` overrides) counts its tests failed, prints `✗ boot: no harness output after 60s; last runtime.log line: "<line>"` from the booted app's `logs/runtime.log`, and ends the child instead of hanging `npx omega test`.
|
|
302
|
+
|
|
298
303
|
## Writing consumer tests
|
|
299
304
|
|
|
300
305
|
In a consumer project, drop files in `test/` (or `test/**`):
|
package/docs/themes.md
CHANGED
|
@@ -19,18 +19,32 @@ The active theme is selected via `config.theme.id` (default `'classy'`). The `gu
|
|
|
19
19
|
Your `src/assets/scss/main.scss` becomes:
|
|
20
20
|
|
|
21
21
|
```scss
|
|
22
|
+
// Generated from `brand.color` in config/omega.json5 by the sass task.
|
|
23
|
+
@use 'brand';
|
|
24
|
+
|
|
22
25
|
@use 'omega-desktop' as * with (
|
|
23
|
-
$primary:
|
|
26
|
+
$primary: brand.$primary,
|
|
24
27
|
// $secondary: #6C757D,
|
|
25
28
|
// $border-radius: 0.5rem,
|
|
26
29
|
);
|
|
27
30
|
|
|
31
|
+
// The runtime --omega-accent ramp, AFTER the framework import so it wins the
|
|
32
|
+
// cascade over the token sheet's placeholders.
|
|
33
|
+
@include brand.ramp;
|
|
34
|
+
|
|
28
35
|
// Custom global styles below...
|
|
29
36
|
main {
|
|
30
37
|
padding: 2rem;
|
|
31
38
|
}
|
|
32
39
|
```
|
|
33
40
|
|
|
41
|
+
`$primary` is `brand.color`, not a literal: the sass task writes
|
|
42
|
+
`dist/assets/scss/_brand.scss` from the resolved config before every compile, so
|
|
43
|
+
recoloring the app is a config edit. Want an accent that DIVERGES from the
|
|
44
|
+
brand? Put the literal back in its place: `$primary: #2563EB,`. No `brand.color`
|
|
45
|
+
set at all, and the partial carries the framework default
|
|
46
|
+
([shared/theming.md](shared/theming.md), [#912](https://github.com/Omega-JS-Stack/omega/issues/912)).
|
|
47
|
+
|
|
34
48
|
That single import gives you:
|
|
35
49
|
- Full Bootstrap 5 (utilities, components, grid, etc.)
|
|
36
50
|
- Classy theme overlays (typography, animations, refined spacing)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omega.js/desktop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "OMEGA desktop framework — build, test, and package Electron apps for macOS, Windows, and Linux",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"./build": "./dist/build.js",
|
|
18
18
|
"./test": "./dist/test/index.js",
|
|
19
19
|
"./hooks/notarize": "./dist/hooks/notarize.js",
|
|
20
|
+
"./hooks/notarize-artifacts": "./dist/hooks/notarize-artifacts.js",
|
|
20
21
|
"./lib/app-state": "./dist/lib/app-state.js",
|
|
21
22
|
"./lib/auto-updater": "./dist/lib/auto-updater.js",
|
|
22
23
|
"./lib/context-menu": "./dist/lib/context-menu.js",
|
|
@@ -35,13 +36,13 @@
|
|
|
35
36
|
"./lib/client-bridge": "./dist/lib/client-bridge.js",
|
|
36
37
|
"./lib/window-manager": "./dist/lib/window-manager.js",
|
|
37
38
|
"./cli": "./dist/cli-run.js",
|
|
39
|
+
"./ensure-target": "./dist/commands/lib/ensure-target.js",
|
|
38
40
|
"./scripts/sync-nvmrc": "./scripts/sync-nvmrc.js"
|
|
39
41
|
},
|
|
40
42
|
"bin": {
|
|
41
43
|
"omega": "./bin/omega",
|
|
42
44
|
"omg": "./bin/omg",
|
|
43
|
-
"mgr": "./bin/omega"
|
|
44
|
-
"omega-desktop": "./bin/omega-desktop"
|
|
45
|
+
"mgr": "./bin/omega"
|
|
45
46
|
},
|
|
46
47
|
"files": [
|
|
47
48
|
"bin/",
|
|
@@ -56,8 +57,8 @@
|
|
|
56
57
|
"replace": {},
|
|
57
58
|
"hooks": {
|
|
58
59
|
"after": [
|
|
59
|
-
"node -e \"require('@omega.js/devkit/
|
|
60
|
-
"node -e \"require('@omega.js/devkit/
|
|
60
|
+
"node -e \"require('@omega.js/devkit/prepare-guard').rewire()\"",
|
|
61
|
+
"node -e \"require('@omega.js/devkit/vendor')()\""
|
|
61
62
|
],
|
|
62
63
|
"afterBlocking": true
|
|
63
64
|
}
|
|
@@ -89,14 +90,19 @@
|
|
|
89
90
|
"package": "@omega.js/web",
|
|
90
91
|
"from": "themes",
|
|
91
92
|
"to": "assets/themes"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"package": "@omega.js/devkit",
|
|
96
|
+
"from": "src/test/fixtures/esm-only-package",
|
|
97
|
+
"to": "vendor/devkit/test/fixtures/esm-only-package"
|
|
92
98
|
}
|
|
93
99
|
]
|
|
94
100
|
},
|
|
95
101
|
"scripts": {
|
|
96
102
|
"start": "npm run prepare:watch",
|
|
97
|
-
"prepare": "node -e \"require('
|
|
103
|
+
"prepare": "node -e \"require('../devkit/tools/prepare-guard').run()\"",
|
|
98
104
|
"prepare:watch": "node -e \"require('prepare-package/watch')()\"",
|
|
99
|
-
"test": "node ./bin/omega
|
|
105
|
+
"test": "node ./bin/omega test"
|
|
100
106
|
},
|
|
101
107
|
"projectScripts": {
|
|
102
108
|
"start": "omega clean && npm run gulp --",
|
|
@@ -130,7 +136,7 @@
|
|
|
130
136
|
"license": "Elastic-2.0",
|
|
131
137
|
"peerDependencies": {
|
|
132
138
|
"electron": ">=42",
|
|
133
|
-
"electron-builder": ">=26",
|
|
139
|
+
"electron-builder": ">=26.16.1",
|
|
134
140
|
"gulp": "^5.0.1"
|
|
135
141
|
},
|
|
136
142
|
"devDependencies": {
|
|
@@ -141,7 +147,7 @@
|
|
|
141
147
|
"@omega.js/monitoring": "*",
|
|
142
148
|
"@omega.js/web": "*",
|
|
143
149
|
"electron": "^42.0.1",
|
|
144
|
-
"electron-builder": "^26.
|
|
150
|
+
"electron-builder": "^26.16.1",
|
|
145
151
|
"firebase-admin": "^13.9.0",
|
|
146
152
|
"prepare-package": "^2.2.0"
|
|
147
153
|
},
|
|
@@ -149,7 +155,7 @@
|
|
|
149
155
|
"@fortawesome/fontawesome-free": "^7.3.0",
|
|
150
156
|
"@inquirer/prompts": "^8.5.2",
|
|
151
157
|
"@octokit/rest": "^22.0.1",
|
|
152
|
-
"@omega.js/client": "0.
|
|
158
|
+
"@omega.js/client": "0.51.0",
|
|
153
159
|
"@popperjs/core": "^2.11.8",
|
|
154
160
|
"@sentry/electron": "^7.13.0",
|
|
155
161
|
"chalk": "^5.6.2",
|
|
@@ -172,11 +178,11 @@
|
|
|
172
178
|
"node-powertools": "^3.0.0",
|
|
173
179
|
"plist": "^4.0.0",
|
|
174
180
|
"sass": "^1.99.0",
|
|
181
|
+
"sharp": "^0.35.3",
|
|
175
182
|
"uuid": "^14.0.0",
|
|
176
183
|
"wonderful-fetch": "^2.0.5",
|
|
177
184
|
"wonderful-version": "^1.3.2",
|
|
178
|
-
"ws": "^8.20.0"
|
|
179
|
-
"yargs": "^18.0.0"
|
|
185
|
+
"ws": "^8.20.0"
|
|
180
186
|
},
|
|
181
187
|
"optionalDependencies": {
|
|
182
188
|
"automately": "^1.0.5"
|
package/bin/omega-desktop
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `.env` → GitHub Actions secrets publisher for the desktop target.
|
|
3
|
-
*
|
|
4
|
-
* Since [#682](https://github.com/Omega-JS-Stack/omega/issues/682) this is a
|
|
5
|
-
* BIND of `@omega.js/devkit/target-secrets`, exactly as web and the extension
|
|
6
|
-
* are: the same derive (the schema's desktop delivery set, #627) → collect (the
|
|
7
|
-
* COMPOSED target env, company ← brand ← target, files only, #678) → guard (the
|
|
8
|
-
* brand's DECLARED repo, never an inferred remote) → publish, over the ONE `gh`
|
|
9
|
-
* transport (values on stdin, never logged), with the same five loud skips. It
|
|
10
|
-
* used to run @octokit/rest + libsodium-wrappers itself on a GH_TOKEN PAT the
|
|
11
|
-
* consumer had to mint; `gh`'s own auth session is the credential now and the
|
|
12
|
-
* sealed-box crypto is the CLI's.
|
|
13
|
-
*
|
|
14
|
-
* What is desktop's OWN is the value resolver, the seam the shared publisher
|
|
15
|
-
* applies between collect and publish: a desktop secret can name a FILE
|
|
16
|
-
* (`CSC_LINK=config/certs/dev-id.p12`, `APPLE_API_KEY=.omega/secrets/key.p8` —
|
|
17
|
-
* target-root relative, falling back to the brand root), and CI needs the
|
|
18
|
-
* BYTES, so an existing file publishes as its base64 contents. The workflow
|
|
19
|
-
* decodes it back to a temp file at job start.
|
|
20
|
-
*
|
|
21
|
-
* Usage:
|
|
22
|
-
* npx omega push-secrets # push every composed desktop key
|
|
23
|
-
* npx omega push-secrets --only=GH_TOKEN,CSC_LINK
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
const path = require('path');
|
|
27
|
-
const fs = require('fs');
|
|
28
|
-
|
|
29
|
-
const Manager = new (require('../build.js'));
|
|
30
|
-
const logger = Manager.logger('push-secrets');
|
|
31
|
-
const { collectTargetSecrets, declaredBrandRepo: declaredRepoFor, publishTargetSecrets } = require('../vendor/devkit/target-secrets.js');
|
|
32
|
-
const { findBrandRoot } = require('../vendor/config/index.js');
|
|
33
|
-
|
|
34
|
-
const TARGET = 'desktop';
|
|
35
|
-
|
|
36
|
-
module.exports = async function (options) {
|
|
37
|
-
options = options || {};
|
|
38
|
-
|
|
39
|
-
return publishEnvSecrets({
|
|
40
|
-
targetDir: process.cwd(),
|
|
41
|
-
logger,
|
|
42
|
-
only: options.only,
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Desktop's value seam: base64 for a value that names an existing file, the
|
|
48
|
-
* value itself otherwise, and null for a key `--only` leaves out (the publisher
|
|
49
|
-
* drops a falsy value, the same way it drops an empty composed one).
|
|
50
|
-
*
|
|
51
|
-
* @param {object} options
|
|
52
|
-
* @param {string} options.targetDir - The target root.
|
|
53
|
-
* @param {string} [options.only] - Comma-separated key names to narrow to.
|
|
54
|
-
* @returns {function} `(value, key) => string|null`
|
|
55
|
-
*/
|
|
56
|
-
function fileValueResolver(options) {
|
|
57
|
-
const { targetDir } = options;
|
|
58
|
-
const only = options.only ? String(options.only).split(',').map((s) => s.trim()).filter(Boolean) : null;
|
|
59
|
-
const brandRoot = findBrandRoot(targetDir);
|
|
60
|
-
|
|
61
|
-
return (value, key) => {
|
|
62
|
-
if (only && !only.includes(key)) return null;
|
|
63
|
-
return fileContentsBase64(value, { targetDir, brandRoot }) || value;
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// The base64 contents of the file `value` names, or null when it names none.
|
|
68
|
-
// Heuristic: a path-shaped value (a separator, or a typical cert/key extension)
|
|
69
|
-
// that resolves to a real file under the target root or the brand root. A
|
|
70
|
-
// path-shaped value that exists nowhere is pushed as-is — it is a string.
|
|
71
|
-
function fileContentsBase64(value, { targetDir, brandRoot }) {
|
|
72
|
-
const looksLikePath = /[/\\]/.test(value) || /\.(p12|pem|cer|p8|provisionprofile|crt|key|json)$/i.test(value);
|
|
73
|
-
if (!looksLikePath) return null;
|
|
74
|
-
|
|
75
|
-
const roots = path.isAbsolute(value) ? [''] : [targetDir, brandRoot].filter(Boolean);
|
|
76
|
-
for (const root of roots) {
|
|
77
|
-
const absolute = path.isAbsolute(value) ? value : path.join(root, value);
|
|
78
|
-
if (fs.existsSync(absolute) && fs.statSync(absolute).isFile()) {
|
|
79
|
-
return fs.readFileSync(absolute).toString('base64');
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* The publishable secrets: the schema's desktop delivery set, valued from the
|
|
88
|
-
* composed env with file-path values resolved to their base64 contents. An
|
|
89
|
-
* empty value never claims a key.
|
|
90
|
-
*
|
|
91
|
-
* @param {string} targetDir - The target root (its .env is the local layer).
|
|
92
|
-
* @param {object} [options]
|
|
93
|
-
* @param {string} [options.only] - Comma-separated key names to narrow to.
|
|
94
|
-
* @returns {Object<string, string>} key → value, ready to publish.
|
|
95
|
-
*/
|
|
96
|
-
function collectEnvSecrets(targetDir, options) {
|
|
97
|
-
return collectTargetSecrets({
|
|
98
|
-
targetDir,
|
|
99
|
-
target: TARGET,
|
|
100
|
-
resolveValue: fileValueResolver({ targetDir, only: (options || {}).only }),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* The brand's OWN repo as `owner/name`, from the composed config. Null when the
|
|
106
|
-
* config declares nothing usable or doesn't load.
|
|
107
|
-
*
|
|
108
|
-
* @param {string} targetDir - The target root.
|
|
109
|
-
* @returns {string|null}
|
|
110
|
-
*/
|
|
111
|
-
function declaredBrandRepo(targetDir) {
|
|
112
|
-
return declaredRepoFor({ targetDir, target: TARGET });
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* The `omega deploy` precheck step: publish the target's composed values as
|
|
117
|
-
* repo Actions secrets.
|
|
118
|
-
*
|
|
119
|
-
* @param {object} options
|
|
120
|
-
* @param {string} options.targetDir - The target root.
|
|
121
|
-
* @param {object} options.logger - `{ log, warn, error }`.
|
|
122
|
-
* @param {object} [options.env] - Env map (default: process.env).
|
|
123
|
-
* @param {string} [options.only] - Comma-separated key names to narrow to (`--only`).
|
|
124
|
-
* @param {function} [options.execFn] - Injectable `gh` exec (tests).
|
|
125
|
-
* @param {function} [options.gitExecFn] - Injectable `git` exec for remote discovery (tests).
|
|
126
|
-
* @returns {{ skipped: string }|{ published: string[], failed: Array<object> }}
|
|
127
|
-
*/
|
|
128
|
-
function publishEnvSecrets(options) {
|
|
129
|
-
options = options || {};
|
|
130
|
-
|
|
131
|
-
return publishTargetSecrets({
|
|
132
|
-
...options,
|
|
133
|
-
target: TARGET,
|
|
134
|
-
resolveValue: fileValueResolver({ targetDir: options.targetDir, only: options.only }),
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
module.exports.collectEnvSecrets = collectEnvSecrets;
|
|
139
|
-
module.exports.declaredBrandRepo = declaredBrandRepo;
|
|
140
|
-
module.exports.fileValueResolver = fileValueResolver;
|
|
141
|
-
module.exports.publishEnvSecrets = publishEnvSecrets;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// deliver-certs — the Apple artifacts reach the target's certs dir on every
|
|
2
|
-
// verb, not only on a manager `manage` run
|
|
3
|
-
// ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)).
|
|
4
|
-
//
|
|
5
|
-
// The copy rules are devkit's; what is pinned here is desktop's framing: which
|
|
6
|
-
// root holds the signing tree (company stamp wins over the brand root) and the
|
|
7
|
-
// no-op when there is no tree at all.
|
|
8
|
-
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const os = require('os');
|
|
12
|
-
const jetpack = require('fs-jetpack');
|
|
13
|
-
|
|
14
|
-
const { deliverTargetCerts, resolveSourceRoot } = require('../../../utils/deliver-certs.js');
|
|
15
|
-
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
16
|
-
|
|
17
|
-
const P12 = path.join('.omega', 'certificates', 'apple', 'certificates', 'DEVELOPER_ID_APPLICATION_G2.p12');
|
|
18
|
-
const quiet = { log() {}, warn() {}, error() {} };
|
|
19
|
-
|
|
20
|
-
// A brand root with a desktop target under it; `tree` seeds the signing tree.
|
|
21
|
-
function stageBrand({ tree = true, companyTree = false } = {}) {
|
|
22
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-certs-'));
|
|
23
|
-
const brandRoot = path.join(root, 'brand');
|
|
24
|
-
const targetDir = path.join(brandRoot, 'targets', 'desktop');
|
|
25
|
-
|
|
26
|
-
jetpack.write(path.join(brandRoot, 'config', 'omega.json5'), "{ brand: { id: 'acme', name: 'Acme', url: 'https://acme.test' }, targets: { desktop: {} } }\n");
|
|
27
|
-
jetpack.dir(targetDir);
|
|
28
|
-
if (tree) jetpack.write(path.join(brandRoot, P12), 'brand-cert');
|
|
29
|
-
|
|
30
|
-
if (companyTree) {
|
|
31
|
-
const companyRoot = path.join(root, 'company');
|
|
32
|
-
jetpack.write(path.join(companyRoot, P12), 'company-cert');
|
|
33
|
-
jetpack.write(path.join(brandRoot, '.omega', 'company.json'), JSON.stringify({ root: companyRoot }));
|
|
34
|
-
return { root, brandRoot, companyRoot, targetDir };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return { root, brandRoot, targetDir };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = defineCases({
|
|
41
|
-
type: 'group',
|
|
42
|
-
layer: 'build',
|
|
43
|
-
description: 'deliver-certs — signing artifacts on every verb (#678)',
|
|
44
|
-
tests: [
|
|
45
|
-
{
|
|
46
|
-
name: 'the brand tree delivers into the target certs dir, and the rerun is current',
|
|
47
|
-
run: (ctx) => {
|
|
48
|
-
const { root, targetDir } = stageBrand();
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const first = deliverTargetCerts({ projectDir: targetDir, logger: quiet });
|
|
52
|
-
ctx.expect(first.copied).toContain('config/certs/developer-id-application.p12');
|
|
53
|
-
ctx.expect(jetpack.read(path.join(targetDir, 'config', 'certs', 'developer-id-application.p12'))).toBe('brand-cert');
|
|
54
|
-
// Signing material can never be committed
|
|
55
|
-
ctx.expect(jetpack.read(path.join(targetDir, 'config', 'certs', '.gitignore'))).toContain('*');
|
|
56
|
-
|
|
57
|
-
const second = deliverTargetCerts({ projectDir: targetDir, logger: quiet });
|
|
58
|
-
ctx.expect(second.copied).toEqual([]);
|
|
59
|
-
ctx.expect(second.current).toContain('config/certs/developer-id-application.p12');
|
|
60
|
-
} finally {
|
|
61
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'the COMPANY stamp wins the source root — company-managed brands share the material',
|
|
67
|
-
run: (ctx) => {
|
|
68
|
-
const { root, brandRoot, companyRoot, targetDir } = stageBrand({ companyTree: true });
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
ctx.expect(resolveSourceRoot(targetDir)).toBe(companyRoot);
|
|
72
|
-
ctx.expect(resolveSourceRoot(targetDir)).not.toBe(brandRoot);
|
|
73
|
-
|
|
74
|
-
deliverTargetCerts({ projectDir: targetDir, logger: quiet });
|
|
75
|
-
ctx.expect(jetpack.read(path.join(targetDir, 'config', 'certs', 'developer-id-application.p12'))).toBe('company-cert');
|
|
76
|
-
} finally {
|
|
77
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'no signing tree → a silent no-op, never a throw (the normal state of a tree-less brand)',
|
|
83
|
-
run: (ctx) => {
|
|
84
|
-
const { root, targetDir } = stageBrand({ tree: false });
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
ctx.expect(deliverTargetCerts({ projectDir: targetDir, logger: quiet })).toBe(null);
|
|
88
|
-
ctx.expect(jetpack.exists(path.join(targetDir, 'config', 'certs'))).toBe(false);
|
|
89
|
-
} finally {
|
|
90
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
});
|