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