@omega.js/desktop 0.1.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 +19 -15
- 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 +3 -3
- 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/dist/commands/test.js
CHANGED
|
@@ -13,9 +13,10 @@ module.exports = async function (options) {
|
|
|
13
13
|
// This process is a TEST RUN, and everything it spawns inherits that. The box's
|
|
14
14
|
// mutating verbs read it and refuse to touch a real runner home
|
|
15
15
|
// ([#337](https://github.com/Omega-JS-Stack/omega/issues/337): a suite once ran
|
|
16
|
-
// a real `runner install` on the signing box). Deliberately
|
|
17
|
-
//
|
|
18
|
-
//
|
|
16
|
+
// a real `runner install` on the signing box). Deliberately a marker about THIS
|
|
17
|
+
// process, never a word about the app under test: the environment the thing
|
|
18
|
+
// being tested RUNS in is `OMEGA_ENVIRONMENT`, and the runners name it on their
|
|
19
|
+
// own children alone (#925).
|
|
19
20
|
process.env.OMEGA_TEST_RUNNER = '1';
|
|
20
21
|
|
|
21
22
|
// Tee all test output to <projectRoot>/logs/test.log (ANSI-stripped) — mirrors
|
|
@@ -1,16 +1,42 @@
|
|
|
1
|
-
// Validate code-signing prerequisites (certs,
|
|
1
|
+
// Validate code-signing prerequisites (certs, env vars).
|
|
2
2
|
//
|
|
3
|
-
// Runs as
|
|
4
|
-
//
|
|
3
|
+
// Runs as the `omega deploy` precheck and inside `omega publish`, both STRICT
|
|
4
|
+
// ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): a deploy is how
|
|
5
|
+
// a signed, notarized release ships, so missing material stops it. Standalone
|
|
6
|
+
// (`npx omega validate-certs`) it reports and exits 0 unless `--strict`.
|
|
7
|
+
//
|
|
8
|
+
// It READS the env, it never derives: the boot populated `CSC_LINK` and
|
|
9
|
+
// `APPLE_API_KEY` from the signing tree through the ONE derivation
|
|
10
|
+
// (utils/load-env.js, `@omega.js/devkit/signing-env`), so this step and the
|
|
11
|
+
// build that follows it judge the same answer. Scanning `config/certs/` for a
|
|
12
|
+
// file nothing pointed at is exactly the second rule that stopped a deploy the
|
|
13
|
+
// build would have signed fine, and it is gone.
|
|
14
|
+
//
|
|
15
|
+
// The mac rungs run on the MAC LEG and nowhere else
|
|
16
|
+
// ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)): the linux and
|
|
17
|
+
// windows legs of the build workflow blank `CSC_LINK` and `APPLE_API_KEY` on
|
|
18
|
+
// purpose, because neither can sign for mac, so checking them there only
|
|
19
|
+
// printed two warnings about material nobody put on that leg. What is an ERROR
|
|
20
|
+
// vs a warning, once the run IS the mac leg:
|
|
21
|
+
// error no signing certificate at all, no Keychain identity on the leg that
|
|
22
|
+
// has no CSC_LINK, a .p12 whose password is missing, an EXPIRED
|
|
23
|
+
// certificate
|
|
24
|
+
// warning a certificate with under 30 days left (it still signs)
|
|
25
|
+
//
|
|
26
|
+
// Expiry is read by the ONE reader every surface uses (`@omega.js/devkit/certs`
|
|
27
|
+
// certificateExpiry, #892), so the manager's certificates walk and this step
|
|
28
|
+
// answer with the same date and the same three rungs.
|
|
5
29
|
//
|
|
6
30
|
// Exit codes:
|
|
7
31
|
// 0 all checks passed
|
|
8
32
|
// 1 failures (only when --strict is passed; otherwise we always exit 0 and just warn)
|
|
9
33
|
|
|
10
34
|
const path = require('path');
|
|
11
|
-
const fs = require('fs');
|
|
12
35
|
const jetpack = require('fs-jetpack');
|
|
13
36
|
const { execute } = require('node-powertools');
|
|
37
|
+
const { certificateExpiry, EXPIRY_WARN_DAYS } = require('../vendor/devkit/certs.js');
|
|
38
|
+
const { signingPathCandidates } = require('../vendor/devkit/signing-env.js');
|
|
39
|
+
const { requiredWhenHolds } = require('../vendor/config/env-rules.js');
|
|
14
40
|
|
|
15
41
|
const Manager = new (require('../build.js'));
|
|
16
42
|
const logger = Manager.logger('validate-certs');
|
|
@@ -27,8 +53,8 @@ module.exports = async function (options) {
|
|
|
27
53
|
|
|
28
54
|
const issues = [];
|
|
29
55
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
56
|
+
// The mac rungs, on the mac leg only (#891): checkMac itself says so and
|
|
57
|
+
// prints the skip line off darwin.
|
|
32
58
|
await checkMac(issues, config);
|
|
33
59
|
|
|
34
60
|
if (platform === 'win32') {
|
|
@@ -56,123 +82,244 @@ module.exports = async function (options) {
|
|
|
56
82
|
return { ok: false, issues };
|
|
57
83
|
};
|
|
58
84
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
/**
|
|
86
|
+
* The mac rungs, in order: the certificate, the notarization key, the team id,
|
|
87
|
+
* and the Keychain identity the build falls back to when the env names no
|
|
88
|
+
* certificate at all. DARWIN ONLY (#891): no other leg ships mac, and the
|
|
89
|
+
* credentials are blanked on the ones that do not.
|
|
90
|
+
*
|
|
91
|
+
* @param {object[]} issues - The run's issue list, appended to.
|
|
92
|
+
* @param {object} config - The resolved omega config.
|
|
93
|
+
* @param {object} [options] - `{ execFn }`, the Keychain query seam.
|
|
94
|
+
*/
|
|
95
|
+
async function checkMac(issues, config, options = {}) {
|
|
96
|
+
if (process.platform !== 'darwin') {
|
|
97
|
+
logger.log('Skipping the macOS signing checks: this leg does not ship mac.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
64
100
|
|
|
65
|
-
|
|
101
|
+
const projectRoot = process.cwd();
|
|
102
|
+
const execFn = options.execFn || execute;
|
|
103
|
+
|
|
104
|
+
// The gate the env schema already uses for the whole mac set (its
|
|
105
|
+
// `requiredWhen`): a brand that DECLARES Apple signing owes these
|
|
106
|
+
// credentials, so a missing one is an error on the leg that signs (#891). A
|
|
107
|
+
// brand that declares none is simply unsigned, and every finding here is a
|
|
108
|
+
// note instead, the Keychain rung included.
|
|
109
|
+
const declaresApple = requiredWhenHolds(config || {}, 'certificates.providers.apple');
|
|
110
|
+
const signs = declaresApple ? 'error' : 'warn';
|
|
111
|
+
|
|
112
|
+
// 1. Developer ID Application .p12. The boot derived CSC_LINK from the
|
|
113
|
+
// signing tree when the tree holds one, so an unset key here means the tree
|
|
114
|
+
// does NOT, and the line names where it looked and the walk that fills it.
|
|
66
115
|
const cscLink = process.env.CSC_LINK;
|
|
67
116
|
if (cscLink) {
|
|
68
|
-
|
|
69
|
-
if (!jetpack.exists(p12Path)) {
|
|
70
|
-
issues.push({ severity: 'error', message: `CSC_LINK points to a missing file: ${p12Path}` });
|
|
71
|
-
} else if (!process.env.CSC_KEY_PASSWORD) {
|
|
72
|
-
issues.push({ severity: 'warn', message: 'CSC_KEY_PASSWORD not set — signing will fail.' });
|
|
73
|
-
} else {
|
|
74
|
-
logger.log(logger.format.green(`✓ Developer ID Application cert at ${path.relative(projectRoot, p12Path)}`));
|
|
75
|
-
}
|
|
76
|
-
} else if (jetpack.exists(certsDir)) {
|
|
77
|
-
// Look for the file even if env not pointed
|
|
78
|
-
const p12Files = jetpack.find(certsDir, { matching: '*.p12', recursive: false }) || [];
|
|
79
|
-
if (p12Files.length === 0) {
|
|
80
|
-
issues.push({ severity: 'warn', message: `No .p12 found in config/certs/ and CSC_LINK not set. (Required for macOS signing.)` });
|
|
81
|
-
} else {
|
|
82
|
-
issues.push({ severity: 'warn', message: `Found .p12 in config/certs/ but CSC_LINK env var is not set. Set CSC_LINK=${path.relative(projectRoot, p12Files[0])}` });
|
|
83
|
-
}
|
|
117
|
+
checkSigningCert(issues, cscLink, projectRoot);
|
|
84
118
|
} else {
|
|
85
|
-
issues.push({ severity:
|
|
119
|
+
issues.push({ severity: signs, message: `CSC_LINK is not set${treeNote(projectRoot, 'CSC_LINK')}, so this build cannot be signed for macOS. Run \`omega manage --service certificates\` to produce the signing material.` });
|
|
86
120
|
}
|
|
87
121
|
|
|
88
|
-
// 2.
|
|
89
|
-
|
|
90
|
-
? (jetpack.find(certsDir, { matching: '*.provisionprofile', recursive: false }) || [])
|
|
91
|
-
: [];
|
|
92
|
-
for (const profPath of provisionFiles) {
|
|
93
|
-
try {
|
|
94
|
-
const parsed = parseProvision(profPath);
|
|
95
|
-
const rel = path.relative(projectRoot, profPath);
|
|
96
|
-
|
|
97
|
-
if (!parsed.parsed || !parsed.raw) {
|
|
98
|
-
issues.push({ severity: 'error', message: `${rel}: could not parse plist payload.` });
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const exp = parsed.parsed.ExpirationDate;
|
|
103
|
-
if (exp && new Date(exp) < new Date()) {
|
|
104
|
-
issues.push({ severity: 'error', message: `${rel}: EXPIRED on ${new Date(exp).toISOString()}.` });
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Days until expiry — warn if < 30 days
|
|
109
|
-
if (exp) {
|
|
110
|
-
const daysLeft = Math.floor((new Date(exp) - new Date()) / (1000 * 60 * 60 * 24));
|
|
111
|
-
if (daysLeft < 30) {
|
|
112
|
-
issues.push({ severity: 'warn', message: `${rel}: expires in ${daysLeft} day(s) — renew soon.` });
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// App ID match — provisioning profiles embed the app-id-prefix.bundle-id.
|
|
117
|
-
// Be lenient: just check the bundle id appears anywhere in the raw plist text.
|
|
118
|
-
if (expectedAppId && !parsed.raw.includes(expectedAppId)) {
|
|
119
|
-
issues.push({ severity: 'error', message: `${rel}: does not contain expected appId "${expectedAppId}". This profile is for a different app.` });
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
logger.log(logger.format.green(`✓ ${rel} (expires ${exp ? new Date(exp).toISOString().slice(0,10) : 'unknown'})`));
|
|
124
|
-
} catch (e) {
|
|
125
|
-
issues.push({ severity: 'error', message: `${path.relative(projectRoot, profPath)}: parse failed: ${e.message}` });
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 3. Notarization API key (.p8)
|
|
122
|
+
// 2. Notarization API key (.p8): derived from the same tree, judged the same
|
|
123
|
+
// way. The non-mac legs of the build workflow carry the base64 secret itself.
|
|
130
124
|
const apiKeyEnv = process.env.APPLE_API_KEY;
|
|
131
125
|
if (apiKeyEnv) {
|
|
132
|
-
|
|
133
|
-
if (!jetpack.exists(p8Path)) {
|
|
134
|
-
issues.push({ severity: 'error', message: `APPLE_API_KEY points to a missing file: ${p8Path}` });
|
|
135
|
-
} else {
|
|
136
|
-
// Check the filename includes the key id (Apple's filename convention is AuthKey_<KEY_ID>.p8).
|
|
137
|
-
const keyId = process.env.APPLE_API_KEY_ID;
|
|
138
|
-
const issuer = process.env.APPLE_API_ISSUER;
|
|
139
|
-
const filenameKeyId = path.basename(p8Path).match(/AuthKey_([A-Z0-9]+)\.p8$/i)?.[1];
|
|
140
|
-
|
|
141
|
-
if (!keyId) {
|
|
142
|
-
issues.push({ severity: 'warn', message: 'APPLE_API_KEY set but APPLE_API_KEY_ID is missing.' });
|
|
143
|
-
} else if (filenameKeyId && filenameKeyId !== keyId) {
|
|
144
|
-
issues.push({ severity: 'warn', message: `APPLE_API_KEY filename suggests key ID "${filenameKeyId}" but APPLE_API_KEY_ID is "${keyId}". Likely a typo.` });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!issuer) {
|
|
148
|
-
issues.push({ severity: 'warn', message: 'APPLE_API_KEY set but APPLE_API_ISSUER is missing.' });
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
logger.log(logger.format.green(`✓ Notarization API key at ${path.relative(projectRoot, p8Path)}`));
|
|
152
|
-
}
|
|
126
|
+
checkNotarizationKey(issues, apiKeyEnv, projectRoot);
|
|
153
127
|
} else {
|
|
154
|
-
issues.push({ severity:
|
|
128
|
+
issues.push({ severity: signs, message: `APPLE_API_KEY is not set${treeNote(projectRoot, 'APPLE_API_KEY')}, so this build cannot be notarized. Set APPLE_API_KEY_ID in the .env and run \`omega manage --service certificates\` to produce the key.` });
|
|
155
129
|
}
|
|
156
130
|
|
|
157
|
-
//
|
|
131
|
+
// 3. Apple Team ID format check
|
|
158
132
|
const teamId = process.env.APPLE_TEAM_ID;
|
|
159
133
|
if (teamId && !/^[A-Z0-9]{10}$/.test(teamId)) {
|
|
160
134
|
issues.push({ severity: 'warn', message: `APPLE_TEAM_ID="${teamId}" doesn't match the expected 10-character format.` });
|
|
161
135
|
}
|
|
162
136
|
|
|
163
|
-
//
|
|
164
|
-
|
|
137
|
+
// 4. macOS Keychain identity: the NO-CSC_LINK path only. A CSC_LINK .p12 is
|
|
138
|
+
// imported by electron-builder into its OWN temporary keychain, so the login
|
|
139
|
+
// keychain says nothing about whether that build can sign. Asking it anyway
|
|
140
|
+
// is what stopped a runner whose file rungs had both just passed (#891).
|
|
141
|
+
// With no CSC_LINK, identity discovery IS the signer, so the missing identity
|
|
142
|
+
// is the same finding an unset key is.
|
|
143
|
+
if (!cscLink) {
|
|
165
144
|
try {
|
|
166
|
-
const out = await
|
|
145
|
+
const out = await execFn('security find-identity -v -p codesigning', { log: false });
|
|
167
146
|
const identities = String(out || '');
|
|
168
147
|
if (!identities.includes('Developer ID Application')) {
|
|
169
|
-
issues.push({ severity:
|
|
148
|
+
issues.push({ severity: signs, message: 'No "Developer ID Application" identity in the macOS Keychain, so a local signed build has nothing to sign with. Run `omega manage --service certificates` (it imports the .p12) or import it via Keychain Access.' });
|
|
170
149
|
} else {
|
|
171
150
|
logger.log(logger.format.green('✓ Keychain has Developer ID Application identity.'));
|
|
172
151
|
}
|
|
173
152
|
} catch (e) {
|
|
174
|
-
issues.push({ severity: '
|
|
153
|
+
issues.push({ severity: 'error', message: `Could not query the macOS Keychain: ${e.message}` });
|
|
175
154
|
}
|
|
155
|
+
} else {
|
|
156
|
+
logger.log('CSC_LINK names the certificate, so electron-builder imports it into its own keychain: no Keychain identity is read.');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Where the derivation LOOKED for a key's file, as a clause to append to the
|
|
162
|
+
* "not set" line. The paths are the signing tree's, company tier first, from
|
|
163
|
+
* the ONE home of them; a target outside a brand has no tree and gets nothing.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} projectRoot - The target root.
|
|
166
|
+
* @param {string} key - 'CSC_LINK' or 'APPLE_API_KEY'.
|
|
167
|
+
* @returns {string} ` (no file at <path> or <path>)`, or ''.
|
|
168
|
+
*/
|
|
169
|
+
function treeNote(projectRoot, key) {
|
|
170
|
+
const candidate = signingPathCandidates({ env: process.env, targetDir: projectRoot })
|
|
171
|
+
.find((entry) => entry.key === key);
|
|
172
|
+
|
|
173
|
+
return candidate ? ` and no file at ${candidate.paths.join(' or ')}` : '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* What a signing credential's env value CARRIES. Both of the mac ones take three
|
|
178
|
+
* shapes: a path to the file, an https URL, or the file ITSELF as inline base64,
|
|
179
|
+
* and the build workflow ships two of them, because only the mac job decodes the
|
|
180
|
+
* base64 secrets to disk before the build
|
|
181
|
+
* ([#872](https://github.com/Omega-JS-Stack/omega/issues/872)). Statting an inline
|
|
182
|
+
* value as a path is what killed the linux leg: a base64 .p12 (or .p8) is
|
|
183
|
+
* thousands of characters and the stat throws ENAMETOOLONG.
|
|
184
|
+
*
|
|
185
|
+
* @param {string} value - The raw env value (CSC_LINK, APPLE_API_KEY).
|
|
186
|
+
* @param {string} projectRoot - The target root, for resolving a relative path.
|
|
187
|
+
* @returns {{ kind: 'url'|'inline'|'file'|'unreadable', path?: string, exists?: boolean, reason?: string }}
|
|
188
|
+
*/
|
|
189
|
+
function describeCertRef(value, projectRoot) {
|
|
190
|
+
if (/^https?:\/\//i.test(value)) {
|
|
191
|
+
return { kind: 'url' };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const candidate = path.isAbsolute(value) ? value : path.join(projectRoot, value);
|
|
195
|
+
|
|
196
|
+
let exists;
|
|
197
|
+
try {
|
|
198
|
+
exists = !!jetpack.exists(candidate);
|
|
199
|
+
} catch (e) {
|
|
200
|
+
// A value no filesystem can even answer for. Base64 is the known case (a
|
|
201
|
+
// name past the OS limit); anything else is reported, never thrown.
|
|
202
|
+
if (looksInline(value)) {
|
|
203
|
+
return { kind: 'inline' };
|
|
204
|
+
}
|
|
205
|
+
return { kind: 'unreadable', reason: e.message };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!exists && looksInline(value)) {
|
|
209
|
+
return { kind: 'inline' };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { kind: 'file', path: candidate, exists };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// A value that is no path at all: one long unbroken run of characters. A real
|
|
216
|
+
// .p12/.p8 path is short and carries a separator; base64 carries neither.
|
|
217
|
+
function looksInline(value) {
|
|
218
|
+
return value.length > 200 && !value.includes('/') && !value.includes(path.sep);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Report on one credential reference, and hand the caller back what it turned out
|
|
223
|
+
* to be so its OWN checks (the cert's password, the key's id and issuer) can run
|
|
224
|
+
* whatever the shape is. A shape with no file skips the file check, because there
|
|
225
|
+
* is no file. Null means it was already reported as a hard failure.
|
|
226
|
+
*
|
|
227
|
+
* @param {object[]} issues - The run's issue list, appended to.
|
|
228
|
+
* @param {object} options - `{ key, value, projectRoot, describe }`.
|
|
229
|
+
* @returns {object|null} the described reference, or null when it failed.
|
|
230
|
+
*/
|
|
231
|
+
function checkCertRef(issues, options) {
|
|
232
|
+
const { key, value, projectRoot, describe } = options;
|
|
233
|
+
const ref = describeCertRef(value, projectRoot);
|
|
234
|
+
|
|
235
|
+
if (ref.kind === 'unreadable') {
|
|
236
|
+
issues.push({ severity: 'error', message: `${key} could not be read as a path: ${ref.reason}` });
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (ref.kind === 'file' && !ref.exists) {
|
|
241
|
+
issues.push({ severity: 'error', message: `${key} points to a missing file: ${ref.path}` });
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (ref.kind === 'inline') {
|
|
246
|
+
logger.log(`${key} carries an inline base64 ${describe} (${value.length} chars), so there is no file to check.`);
|
|
247
|
+
} else if (ref.kind === 'url') {
|
|
248
|
+
logger.log(`${key} carries a ${describe} URL, so there is no file to check.`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return ref;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The Developer ID Application certificate (CSC_LINK), whatever shape it arrives
|
|
256
|
+
* in. Only a file is checkable as a file; every shape still needs the password
|
|
257
|
+
* that decrypts it.
|
|
258
|
+
*/
|
|
259
|
+
function checkSigningCert(issues, value, projectRoot) {
|
|
260
|
+
const cert = checkCertRef(issues, { key: 'CSC_LINK', value, projectRoot, describe: 'certificate' });
|
|
261
|
+
if (!cert) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!process.env.CSC_KEY_PASSWORD) {
|
|
266
|
+
issues.push({ severity: 'error', message: 'CSC_KEY_PASSWORD is not set, so the signing certificate cannot be opened and signing WILL fail.' });
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (cert.kind !== 'file') {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const rel = pathLabel(projectRoot, cert.path);
|
|
275
|
+
|
|
276
|
+
// The same three rungs the manager's certificates walk reports, over the same
|
|
277
|
+
// reader (#892): fine, under 30 days warns, expired errors.
|
|
278
|
+
let expiry;
|
|
279
|
+
try {
|
|
280
|
+
expiry = certificateExpiry(cert.path, { password: process.env.CSC_KEY_PASSWORD });
|
|
281
|
+
} catch (e) {
|
|
282
|
+
issues.push({ severity: 'error', message: `${rel}: ${e.message}` });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const rung = expiryRung(rel, expiry);
|
|
287
|
+
if (rung) {
|
|
288
|
+
issues.push(rung);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
logger.log(logger.format.green(`✓ Developer ID Application cert at ${rel} (expires ${expiry.expiresAt.toISOString().slice(0, 10)}, ${expiry.daysLeft} days)`));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* The App Store Connect API key (APPLE_API_KEY). Apple's filename convention is
|
|
297
|
+
* `AuthKey_<KEY_ID>.p8`, so the id/filename cross-check only exists for a FILE:
|
|
298
|
+
* an inline key has no filename. The id and the issuer are wanted either way,
|
|
299
|
+
* because notarytool takes all three.
|
|
300
|
+
*/
|
|
301
|
+
function checkNotarizationKey(issues, value, projectRoot) {
|
|
302
|
+
const apiKey = checkCertRef(issues, { key: 'APPLE_API_KEY', value, projectRoot, describe: 'key' });
|
|
303
|
+
if (!apiKey) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const keyId = process.env.APPLE_API_KEY_ID;
|
|
308
|
+
const issuer = process.env.APPLE_API_ISSUER;
|
|
309
|
+
const filenameKeyId = apiKey.kind === 'file' ? path.basename(apiKey.path).match(/AuthKey_([A-Z0-9]+)\.p8$/i)?.[1] : null;
|
|
310
|
+
|
|
311
|
+
if (!keyId) {
|
|
312
|
+
issues.push({ severity: 'warn', message: 'APPLE_API_KEY set but APPLE_API_KEY_ID is missing.' });
|
|
313
|
+
} else if (filenameKeyId && filenameKeyId !== keyId) {
|
|
314
|
+
issues.push({ severity: 'warn', message: `APPLE_API_KEY filename suggests key ID "${filenameKeyId}" but APPLE_API_KEY_ID is "${keyId}". Likely a typo.` });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!issuer) {
|
|
318
|
+
issues.push({ severity: 'warn', message: 'APPLE_API_KEY set but APPLE_API_ISSUER is missing.' });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (apiKey.kind === 'file') {
|
|
322
|
+
logger.log(logger.format.green(`✓ Notarization API key at ${pathLabel(projectRoot, apiKey.path)}`));
|
|
176
323
|
}
|
|
177
324
|
}
|
|
178
325
|
|
|
@@ -193,9 +340,11 @@ function checkWindows(issues, strategy, config) {
|
|
|
193
340
|
}
|
|
194
341
|
|
|
195
342
|
if (strategy === 'cloud') {
|
|
196
|
-
|
|
343
|
+
// The same key sign-windows and the build read: a platform setting, so it
|
|
344
|
+
// sits under `platforms`, never under `targets` (#872).
|
|
345
|
+
const provider = config.platforms?.windows?.signing?.cloud?.provider;
|
|
197
346
|
if (!provider) {
|
|
198
|
-
issues.push({ severity: 'error', message: 'Cloud signing strategy selected but no provider set (
|
|
347
|
+
issues.push({ severity: 'error', message: 'Cloud signing strategy selected but no provider set (platforms.windows.signing.cloud.provider in omega.json5).' });
|
|
199
348
|
return;
|
|
200
349
|
}
|
|
201
350
|
const required = {
|
|
@@ -216,23 +365,50 @@ function checkWindows(issues, strategy, config) {
|
|
|
216
365
|
issues.push({ severity: 'error', message: `Unknown Windows signing strategy: ${strategy}` });
|
|
217
366
|
}
|
|
218
367
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
368
|
+
/**
|
|
369
|
+
* What to CALL a file in a message: target-relative when it sits inside the
|
|
370
|
+
* target, absolute otherwise. The signing tree lives outside the target, so a
|
|
371
|
+
* relative label there would be a run of `../` nobody can read.
|
|
372
|
+
*
|
|
373
|
+
* @param {string} projectRoot - The target root.
|
|
374
|
+
* @param {string} filePath - Absolute path to the file.
|
|
375
|
+
* @returns {string}
|
|
376
|
+
*/
|
|
377
|
+
function pathLabel(projectRoot, filePath) {
|
|
378
|
+
const rel = path.relative(projectRoot, filePath);
|
|
379
|
+
return rel.startsWith('..') ? filePath : rel;
|
|
380
|
+
}
|
|
223
381
|
|
|
224
|
-
|
|
225
|
-
|
|
382
|
+
/**
|
|
383
|
+
* The ONE verdict on a date: expired errors, under the shared renewal window
|
|
384
|
+
* warns, anything else is fine. The same three rungs the manager's certificates
|
|
385
|
+
* walk prints, from the same constant (#892).
|
|
386
|
+
*
|
|
387
|
+
* @param {string} label - What the message names (a target-relative path).
|
|
388
|
+
* @param {{ expiresAt: Date, daysLeft: number }|null} expiry
|
|
389
|
+
* @returns {{ severity: 'error'|'warn', message: string }|null} null when it is fine.
|
|
390
|
+
*/
|
|
391
|
+
function expiryRung(label, expiry) {
|
|
392
|
+
if (!expiry) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
226
395
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
parsed = require('plist').parse(xml);
|
|
231
|
-
} catch (e) {
|
|
232
|
-
return { parsed: null, raw: xml };
|
|
396
|
+
if (expiry.daysLeft < 0) {
|
|
397
|
+
return { severity: 'error', message: `${label}: EXPIRED on ${expiry.expiresAt.toISOString().slice(0, 10)} (${Math.abs(expiry.daysLeft)} days ago). Run \`omega manage --service certificates\` to renew it.` };
|
|
233
398
|
}
|
|
234
|
-
|
|
399
|
+
|
|
400
|
+
if (expiry.daysLeft < EXPIRY_WARN_DAYS) {
|
|
401
|
+
return { severity: 'warn', message: `${label}: expires in ${expiry.daysLeft} day(s), renew soon.` };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return null;
|
|
235
405
|
}
|
|
236
406
|
|
|
237
407
|
// Exported for tests.
|
|
238
|
-
module.exports.
|
|
408
|
+
module.exports.describeCertRef = describeCertRef;
|
|
409
|
+
module.exports.checkCertRef = checkCertRef;
|
|
410
|
+
module.exports.checkSigningCert = checkSigningCert;
|
|
411
|
+
module.exports.checkNotarizationKey = checkNotarizationKey;
|
|
412
|
+
module.exports.checkWindows = checkWindows;
|
|
413
|
+
module.exports.checkMac = checkMac;
|
|
414
|
+
module.exports.expiryRung = expiryRung;
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
<!-- Per-page CSS — present only on this view (compiled from src/assets/scss/pages/{{ page.name }}.scss). -->
|
|
12
12
|
<link href="../../assets/css/components/{{ page.name }}.bundle.css?cb={{ cacheBust }}" rel="stylesheet">
|
|
13
|
+
|
|
14
|
+
<!-- The ONE build snapshot (#743): dist/build.js, the same file every OMEGA
|
|
15
|
+
browser surface loads, ahead of any bundle that reads OMEGA_BUILD_JSON. -->
|
|
16
|
+
<script src="../../build.js?cb={{ cacheBust }}"></script>
|
|
13
17
|
</head>
|
|
14
18
|
<body>
|
|
15
19
|
<!--
|