@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
|
@@ -7,13 +7,34 @@ const fs = require('fs');
|
|
|
7
7
|
const os = require('os');
|
|
8
8
|
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
9
9
|
|
|
10
|
-
function stageConsumer(jsonText) {
|
|
10
|
+
function stageConsumer(jsonText, overlays) {
|
|
11
11
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-getconfig-'));
|
|
12
12
|
fs.mkdirSync(path.join(tmp, 'config'), { recursive: true });
|
|
13
13
|
fs.writeFileSync(path.join(tmp, 'config', 'omega.json5'), jsonText);
|
|
14
|
+
for (const [environment, contents] of Object.entries(overlays || {})) {
|
|
15
|
+
fs.writeFileSync(path.join(tmp, 'config', `omega.${environment}.json5`), contents);
|
|
16
|
+
}
|
|
14
17
|
return tmp;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
// Run `fn` with the build-mode flag and this machine's ambient environment
|
|
21
|
+
// stated the way a real run states them; the originals come back after.
|
|
22
|
+
function withEnv(vars, fn) {
|
|
23
|
+
const saved = Object.keys(vars).map((key) => [key, process.env[key]]);
|
|
24
|
+
try {
|
|
25
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
26
|
+
if (value === undefined) delete process.env[key];
|
|
27
|
+
else process.env[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return fn();
|
|
30
|
+
} finally {
|
|
31
|
+
for (const [key, value] of saved) {
|
|
32
|
+
if (value === undefined) delete process.env[key];
|
|
33
|
+
else process.env[key] = value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
17
38
|
function loadConfigInDir(dir) {
|
|
18
39
|
const oldCwd = process.cwd();
|
|
19
40
|
// Bust the build module cache since it's a singleton.
|
|
@@ -37,14 +58,14 @@ module.exports = defineCases({
|
|
|
37
58
|
{
|
|
38
59
|
name: 'derives appId from the BRAND: reverse-domain of brand.url, else app.<brand.id> (friction #18)',
|
|
39
60
|
run: (ctx) => {
|
|
40
|
-
const withUrl = stageConsumer(`{ brand: { id: 'somiibo', name: 'Somiibo', url: 'https://www.somiibo.com' }, targets: { desktop: {} } }`);
|
|
61
|
+
const withUrl = stageConsumer(`{ brand: { id: 'somiibo', name: 'Somiibo', url: 'https://www.somiibo.com' }, targets: { desktop: { type: 'desktop' } } }`);
|
|
41
62
|
try {
|
|
42
63
|
ctx.expect(loadConfigInDir(withUrl).app.appId).toBe('com.somiibo');
|
|
43
64
|
} finally {
|
|
44
65
|
fs.rmSync(withUrl, { recursive: true, force: true });
|
|
45
66
|
}
|
|
46
67
|
|
|
47
|
-
const noUrl = stageConsumer(`{ brand: { id: 'somiibo', name: 'Somiibo' }, targets: { desktop: {} } }`);
|
|
68
|
+
const noUrl = stageConsumer(`{ brand: { id: 'somiibo', name: 'Somiibo' }, targets: { desktop: { type: 'desktop' } } }`);
|
|
48
69
|
try {
|
|
49
70
|
ctx.expect(loadConfigInDir(noUrl).app.appId).toBe('app.somiibo');
|
|
50
71
|
} finally {
|
|
@@ -52,10 +73,58 @@ module.exports = defineCases({
|
|
|
52
73
|
}
|
|
53
74
|
},
|
|
54
75
|
},
|
|
76
|
+
{
|
|
77
|
+
// #909: the certificates service registers
|
|
78
|
+
// `<certificates.providers.apple.bundleIdPrefix>.<brand.id>` (dashes as
|
|
79
|
+
// dots), so a brand whose config carries a prefix (its own, or the
|
|
80
|
+
// company layer's) signs under that same id. The URL host is what a
|
|
81
|
+
// brand with no prefix still derives from, and a GitHub repo URL is
|
|
82
|
+
// exactly why that fallback cannot be the rule.
|
|
83
|
+
name: 'derives appId from certificates.providers.apple.bundleIdPrefix when the config carries one (#909)',
|
|
84
|
+
run: (ctx) => {
|
|
85
|
+
const withPrefix = stageConsumer(`{
|
|
86
|
+
brand: { id: 'my-app', name: 'My App', url: 'https://github.com/acme/my-app' },
|
|
87
|
+
certificates: { providers: { apple: { bundleIdPrefix: 'com.itwcreativeworks' } } },
|
|
88
|
+
targets: { desktop: { type: 'desktop' } },
|
|
89
|
+
}`);
|
|
90
|
+
try {
|
|
91
|
+
ctx.expect(loadConfigInDir(withPrefix).app.appId).toBe('com.itwcreativeworks.my.app');
|
|
92
|
+
} finally {
|
|
93
|
+
fs.rmSync(withPrefix, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const noPrefix = stageConsumer(`{
|
|
97
|
+
brand: { id: 'my-app', name: 'My App', url: 'https://github.com/acme/my-app' },
|
|
98
|
+
targets: { desktop: { type: 'desktop' } },
|
|
99
|
+
}`);
|
|
100
|
+
try {
|
|
101
|
+
ctx.expect(loadConfigInDir(noPrefix).app.appId).toBe('com.github');
|
|
102
|
+
} finally {
|
|
103
|
+
fs.rmSync(noPrefix, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
// ONE derivation, @omega.js/config's (#909). A hyphenated or uppercase
|
|
109
|
+
// host is exactly where reversing the string by hand and the helper part
|
|
110
|
+
// ways, and a bundle id carrying a hyphen is not a bundle id.
|
|
111
|
+
name: 'the url fallback is @omega.js/config\'s own derivation, hyphens and case included (#909)',
|
|
112
|
+
run: (ctx) => {
|
|
113
|
+
const { deriveBundleIdPrefix } = require('../../../vendor/config/index.js');
|
|
114
|
+
const url = 'https://My-App.Example.com';
|
|
115
|
+
const dir = stageConsumer(`{ brand: { id: 'my-app', name: 'My App', url: '${url}' }, targets: { desktop: { type: 'desktop' } } }`);
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
ctx.expect(loadConfigInDir(dir).app.appId).toBe(deriveBundleIdPrefix(url));
|
|
119
|
+
} finally {
|
|
120
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
},
|
|
55
124
|
{
|
|
56
125
|
name: 'derives productName from brand.name when not set',
|
|
57
126
|
run: (ctx) => {
|
|
58
|
-
const tmp = stageConsumer(`{ brand: { id: 'somiibo', name: 'Somiibo' }, targets: { desktop: {} } }`);
|
|
127
|
+
const tmp = stageConsumer(`{ brand: { id: 'somiibo', name: 'Somiibo' }, targets: { desktop: { type: 'desktop' } } }`);
|
|
59
128
|
try {
|
|
60
129
|
const cfg = loadConfigInDir(tmp);
|
|
61
130
|
ctx.expect(cfg.app.productName).toBe('Somiibo');
|
|
@@ -70,6 +139,7 @@ module.exports = defineCases({
|
|
|
70
139
|
const tmp = stageConsumer(`{
|
|
71
140
|
brand: { id: 'somiibo', name: 'Somiibo' },
|
|
72
141
|
targets: { desktop: {
|
|
142
|
+
type: 'desktop',
|
|
73
143
|
app: { appId: 'com.custom.id', productName: 'Custom Name' },
|
|
74
144
|
} },
|
|
75
145
|
}`);
|
|
@@ -88,14 +158,15 @@ module.exports = defineCases({
|
|
|
88
158
|
const tmp = stageConsumer(`{
|
|
89
159
|
brand: { id: 'somiibo', name: 'Somiibo' },
|
|
90
160
|
targets: { desktop: {
|
|
161
|
+
type: 'desktop',
|
|
91
162
|
startup: { mode: 'hidden' },
|
|
92
|
-
platforms: {
|
|
163
|
+
platforms: { windows: { signing: { strategy: 'cloud' } } },
|
|
93
164
|
} },
|
|
94
165
|
}`);
|
|
95
166
|
try {
|
|
96
167
|
const cfg = loadConfigInDir(tmp);
|
|
97
168
|
ctx.expect(cfg.startup.mode).toBe('hidden');
|
|
98
|
-
ctx.expect(cfg.platforms.
|
|
169
|
+
ctx.expect(cfg.platforms.windows.signing.strategy).toBe('cloud');
|
|
99
170
|
} finally {
|
|
100
171
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
101
172
|
}
|
|
@@ -108,6 +179,7 @@ module.exports = defineCases({
|
|
|
108
179
|
brand: { id: 'somiibo', name: 'Somiibo' },
|
|
109
180
|
monitoring: { providers: { sentry: { dsn: 'https://shared.example.com' } } },
|
|
110
181
|
targets: { desktop: {
|
|
182
|
+
type: 'desktop',
|
|
111
183
|
monitoring: { providers: { sentry: { dsn: 'https://desktop-only.example.com' } } },
|
|
112
184
|
} },
|
|
113
185
|
}`);
|
|
@@ -119,6 +191,44 @@ module.exports = defineCases({
|
|
|
119
191
|
}
|
|
120
192
|
},
|
|
121
193
|
},
|
|
194
|
+
{
|
|
195
|
+
// #856: the environment overlay a BUILD composes is the one the lane
|
|
196
|
+
// names, not the one the machine answers. OMEGA_BUILD_MODE is the lane's
|
|
197
|
+
// own word for a production bake, and a dev boot (no flag) is development
|
|
198
|
+
// ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)). The
|
|
199
|
+
// resolved config carries the word it composed under, as `environment`,
|
|
200
|
+
// the build fact every OMEGA surface spells the same way (#896).
|
|
201
|
+
name: 'a PRODUCTION build composes the production overlay, a dev boot the ambient one (#856)',
|
|
202
|
+
run: (ctx) => {
|
|
203
|
+
const tmp = stageConsumer(
|
|
204
|
+
`{ brand: { id: 'somiibo', name: 'Somiibo' }, targets: { desktop: { type: 'desktop' } } }`,
|
|
205
|
+
{
|
|
206
|
+
production: `{ targets: { desktop: { app: { productName: 'Somiibo Production' } } } }`,
|
|
207
|
+
development: `{ targets: { desktop: { app: { productName: 'Somiibo Dev' } } } }`,
|
|
208
|
+
},
|
|
209
|
+
);
|
|
210
|
+
try {
|
|
211
|
+
// A fresh lane each time: OMEGA_ENVIRONMENT is the ONE input (#817)
|
|
212
|
+
// and src/build.js sets it at load from the build-mode flag, so each
|
|
213
|
+
// sub-case starts from a process that has not been told anything.
|
|
214
|
+
const freshLane = { OMEGA_ENVIRONMENT: undefined, OMEGA_TEST_MODE: undefined, ENVIRONMENT: undefined, FUNCTIONS_EMULATOR: undefined, TERM_PROGRAM: undefined };
|
|
215
|
+
|
|
216
|
+
const built = withEnv({ ...freshLane, OMEGA_BUILD_MODE: 'true' }, () => loadConfigInDir(tmp));
|
|
217
|
+
ctx.expect(built.app.productName).toBe('Somiibo Production');
|
|
218
|
+
ctx.expect(built.environment).toBe('production');
|
|
219
|
+
|
|
220
|
+
// A dev boot names nothing, and resolves development rather than the
|
|
221
|
+
// machine-sniffed answer its old copy gave (#817): the same run used
|
|
222
|
+
// to compose the PRODUCTION overlay in a terminal that did not look
|
|
223
|
+
// like one.
|
|
224
|
+
const dev = withEnv({ ...freshLane, OMEGA_BUILD_MODE: undefined }, () => loadConfigInDir(tmp));
|
|
225
|
+
ctx.expect(dev.app.productName).toBe('Somiibo Dev');
|
|
226
|
+
ctx.expect(dev.environment).toBe('development');
|
|
227
|
+
} finally {
|
|
228
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
},
|
|
122
232
|
{
|
|
123
233
|
name: 'returns seeded {brand:{}, app:{}} when config file missing',
|
|
124
234
|
run: (ctx) => {
|
|
@@ -126,8 +236,10 @@ module.exports = defineCases({
|
|
|
126
236
|
// deref config.brand.X / config.app.X without optional-chaining at every site.
|
|
127
237
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-getconfig-empty-'));
|
|
128
238
|
try {
|
|
129
|
-
|
|
130
|
-
|
|
239
|
+
// A fresh lane, so the seeded shape is stated against a known input.
|
|
240
|
+
const cfg = withEnv({ OMEGA_ENVIRONMENT: undefined, OMEGA_BUILD_MODE: undefined }, () => loadConfigInDir(tmp));
|
|
241
|
+
// `environment` rides on every resolved config as the build fact (#817/#896).
|
|
242
|
+
ctx.expect(cfg).toEqual({ environment: 'development', brand: {}, app: {} });
|
|
131
243
|
} finally {
|
|
132
244
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
133
245
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
// Build-layer tests for src/utils/github.js
|
|
2
|
-
// Repo DISCOVERY is not this module's job
|
|
3
|
-
// releases repo come from @omega.js/config, never a package.json or a git
|
|
1
|
+
// Build-layer tests for src/utils/github.js: the octokit factory, and nothing
|
|
2
|
+
// else. Repo DISCOVERY is not this module's job (#799): the source repo and the
|
|
3
|
+
// releases repo come from @omega.js/config, never a package.json or a git
|
|
4
|
+
// remote. Repo CREATION is not either (#883): `@omega.js/devkit/github-repo`'s
|
|
5
|
+
// ensureRepo is the one home, so a bug in it is fixed once for the manage walk,
|
|
6
|
+
// the web deploy and the release precheck alike.
|
|
4
7
|
|
|
5
8
|
const path = require('path');
|
|
6
9
|
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
@@ -8,17 +11,20 @@ const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
|
8
11
|
module.exports = defineCases({
|
|
9
12
|
type: 'suite',
|
|
10
13
|
layer: 'build',
|
|
11
|
-
description: 'utils/github
|
|
14
|
+
description: 'utils/github: the octokit factory, and only that',
|
|
12
15
|
tests: [
|
|
13
16
|
{
|
|
14
|
-
name: 'github utils exports the octokit surface
|
|
17
|
+
name: 'github utils exports the octokit surface, no discovery (#799) and no ensure (#883)',
|
|
15
18
|
run: (ctx) => {
|
|
16
19
|
const mod = require(path.join(__dirname, '..', '..', '..', 'utils', 'github.js'));
|
|
17
20
|
ctx.expect(typeof mod.getOctokit).toBe('function');
|
|
18
|
-
ctx.expect(typeof mod.ensureRepo).toBe('function');
|
|
19
21
|
// The git-remote fallback is gone: in a brand monorepo it answered the
|
|
20
22
|
// ENCLOSING repo, so every release verb targeted the wrong owner.
|
|
21
23
|
ctx.expect(mod.discoverRepo).toBeUndefined();
|
|
24
|
+
// And the second ensureRepo is gone with it: one implementation, in
|
|
25
|
+
// devkit, so a repo this framework creates matches every other.
|
|
26
|
+
ctx.expect(mod.ensureRepo).toBeUndefined();
|
|
27
|
+
ctx.expect(typeof require('../../../vendor/devkit/github-repo.js').ensureRepo).toBe('function');
|
|
22
28
|
},
|
|
23
29
|
},
|
|
24
30
|
{
|
|
@@ -22,13 +22,15 @@ module.exports = defineCases({
|
|
|
22
22
|
{
|
|
23
23
|
name: 'the stamp rides the build blob, never the client config',
|
|
24
24
|
run: (ctx) => {
|
|
25
|
-
const { composeBuildJson } = require(path.join(FRAMEWORK_ROOT, 'src', 'gulp', 'tasks', 'bundle.js'));
|
|
25
|
+
const { composeBuildJson, buildFacts } = require(path.join(FRAMEWORK_ROOT, 'src', 'gulp', 'tasks', 'bundle.js'));
|
|
26
26
|
|
|
27
|
+
const pkg = { version: '3.1.4' };
|
|
28
|
+
const mode = { environment: 'production', build: true, publish: false };
|
|
27
29
|
const buildJson = composeBuildJson({
|
|
28
30
|
config: { brand: { id: 'paperloom' } },
|
|
29
|
-
dev: null,
|
|
30
|
-
pkg
|
|
31
|
-
mode
|
|
31
|
+
facts: buildFacts({ pkg, mode, dev: null }),
|
|
32
|
+
pkg,
|
|
33
|
+
mode,
|
|
32
34
|
license: { status: 'licensed', payments: 'live', attribution: 'removed' },
|
|
33
35
|
});
|
|
34
36
|
|
|
@@ -3,6 +3,51 @@
|
|
|
3
3
|
const Manager = require('../../../build.js');
|
|
4
4
|
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
5
5
|
|
|
6
|
+
// The one input and the two lane flags that decide it, saved and restored
|
|
7
|
+
// around every case that touches them (#817).
|
|
8
|
+
const LANE_KEYS = ['OMEGA_ENVIRONMENT', 'OMEGA_BUILD_MODE', 'OMEGA_TEST_MODE', 'NODE_ENV'];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Run `fn` with exactly `vars` set on the lane keys (null clears them all).
|
|
12
|
+
* @param {object|null} vars - the lane env for this case.
|
|
13
|
+
* @param {function} fn - the body.
|
|
14
|
+
* @returns {*} whatever `fn` returns.
|
|
15
|
+
*/
|
|
16
|
+
function withEnvironment(vars, fn) {
|
|
17
|
+
const saved = {};
|
|
18
|
+
for (const key of LANE_KEYS) saved[key] = process.env[key];
|
|
19
|
+
try {
|
|
20
|
+
for (const key of LANE_KEYS) delete process.env[key];
|
|
21
|
+
Object.assign(process.env, vars || {});
|
|
22
|
+
return fn();
|
|
23
|
+
} finally {
|
|
24
|
+
for (const key of LANE_KEYS) {
|
|
25
|
+
if (saved[key] === undefined) delete process.env[key];
|
|
26
|
+
else process.env[key] = saved[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What src/build.js resolves at LOAD for a given lane. build.js sets the input
|
|
33
|
+
* once, at require time, so this loads a fresh copy of it per lane.
|
|
34
|
+
* @param {object} vars - the lane env.
|
|
35
|
+
* @returns {string} the resolved environment.
|
|
36
|
+
*/
|
|
37
|
+
function laneEnvironment(vars) {
|
|
38
|
+
const buildPath = require.resolve('../../../build.js');
|
|
39
|
+
return withEnvironment(vars, () => {
|
|
40
|
+
const saved = require.cache[buildPath];
|
|
41
|
+
delete require.cache[buildPath];
|
|
42
|
+
try {
|
|
43
|
+
return require(buildPath).getEnvironment();
|
|
44
|
+
} finally {
|
|
45
|
+
if (saved) require.cache[buildPath] = saved;
|
|
46
|
+
else delete require.cache[buildPath];
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
6
51
|
module.exports = defineCases({
|
|
7
52
|
type: 'group', // independent tests; run all even on failure
|
|
8
53
|
layer: 'build',
|
|
@@ -23,96 +68,71 @@ module.exports = defineCases({
|
|
|
23
68
|
ctx.expect(mode).toHaveProperty('publish');
|
|
24
69
|
ctx.expect(mode).toHaveProperty('server');
|
|
25
70
|
ctx.expect(mode).toHaveProperty('environment');
|
|
26
|
-
ctx.expect(['development', 'production']).toContain(mode.environment);
|
|
71
|
+
ctx.expect(['development', 'testing', 'production']).toContain(mode.environment);
|
|
27
72
|
},
|
|
28
73
|
},
|
|
29
74
|
{
|
|
30
|
-
|
|
75
|
+
// The environment reads ONE input, `OMEGA_ENVIRONMENT`
|
|
76
|
+
// ([#817](https://github.com/Omega-JS-Stack/omega/issues/817)), and the
|
|
77
|
+
// lane that boots or builds this app sets it (src/build.js does, at load).
|
|
78
|
+
// Nothing sniffs NODE_ENV, app.isPackaged or OMEGA_TEST_MODE any more.
|
|
79
|
+
name: 'getEnvironment answers the one input, for each of the three names',
|
|
31
80
|
run: (ctx) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
} finally {
|
|
39
|
-
if (prevTest === undefined) delete process.env.OMEGA_TEST_MODE; else process.env.OMEGA_TEST_MODE = prevTest;
|
|
40
|
-
if (prevBuild === undefined) delete process.env.OMEGA_BUILD_MODE; else process.env.OMEGA_BUILD_MODE = prevBuild;
|
|
41
|
-
}
|
|
81
|
+
withEnvironment(null, () => {
|
|
82
|
+
for (const name of ['development', 'testing', 'production']) {
|
|
83
|
+
process.env.OMEGA_ENVIRONMENT = name;
|
|
84
|
+
ctx.expect(Manager.getEnvironment()).toBe(name);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
42
87
|
},
|
|
43
88
|
},
|
|
44
89
|
{
|
|
45
|
-
|
|
90
|
+
// The bug this replaced: with no signal desktop's own copy answered
|
|
91
|
+
// 'production', so a plain `npm start` bundled itself as a production
|
|
92
|
+
// artifact, while @omega.js/extension's copy of the same function
|
|
93
|
+
// answered 'development' from the same inputs (#817).
|
|
94
|
+
name: 'the build lane resolves production, a dev boot resolves development',
|
|
46
95
|
run: (ctx) => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
96
|
+
const buildLane = laneEnvironment({ OMEGA_BUILD_MODE: 'true' });
|
|
97
|
+
ctx.expect(buildLane).toBe('production');
|
|
98
|
+
|
|
99
|
+
const devBoot = laneEnvironment({});
|
|
100
|
+
ctx.expect(devBoot).toBe('development');
|
|
101
|
+
|
|
102
|
+
// A lane that already named one keeps it, EXCEPT a build lane: a
|
|
103
|
+
// production build spawned from a test run still bakes production.
|
|
104
|
+
ctx.expect(laneEnvironment({ OMEGA_ENVIRONMENT: 'testing' })).toBe('testing');
|
|
105
|
+
ctx.expect(laneEnvironment({ OMEGA_ENVIRONMENT: 'testing', OMEGA_BUILD_MODE: 'true' })).toBe('production');
|
|
57
106
|
},
|
|
58
107
|
},
|
|
59
108
|
{
|
|
60
|
-
|
|
61
|
-
// no OMEGA_TEST_MODE/OMEGA_BUILD_MODE, no NODE_ENV=development). @omega.js/desktop's deployed RUNTIME can
|
|
62
|
-
// legitimately reach here without a dev signal (a shipped binary), so production is the
|
|
63
|
-
// safe default. NODE_ENV=development is the explicit dev override (tested separately).
|
|
64
|
-
name: 'getEnvironment defaults to production when no dev/test signal is present',
|
|
109
|
+
name: 'no input at all is a loud error naming OMEGA_ENVIRONMENT, never a default',
|
|
65
110
|
run: (ctx) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
ctx.expect(Manager.getEnvironment()).toBe('production');
|
|
74
|
-
// NODE_ENV=development is the explicit local-dev override.
|
|
75
|
-
process.env.NODE_ENV = 'development';
|
|
76
|
-
ctx.expect(Manager.getEnvironment()).toBe('development');
|
|
77
|
-
} finally {
|
|
78
|
-
if (prevTest !== undefined) process.env.OMEGA_TEST_MODE = prevTest;
|
|
79
|
-
if (prevBuild !== undefined) process.env.OMEGA_BUILD_MODE = prevBuild;
|
|
80
|
-
if (prevNode === undefined) delete process.env.NODE_ENV; else process.env.NODE_ENV = prevNode;
|
|
81
|
-
}
|
|
111
|
+
withEnvironment(null, () => {
|
|
112
|
+
delete process.env.OMEGA_ENVIRONMENT;
|
|
113
|
+
ctx.expect(() => Manager.getEnvironment()).toThrow(/OMEGA_ENVIRONMENT/);
|
|
114
|
+
process.env.OMEGA_ENVIRONMENT = 'staging';
|
|
115
|
+
ctx.expect(() => Manager.getEnvironment()).toThrow(/OMEGA_ENVIRONMENT/);
|
|
116
|
+
});
|
|
82
117
|
},
|
|
83
118
|
},
|
|
84
119
|
{
|
|
85
|
-
// The core invariant
|
|
86
|
-
//
|
|
87
|
-
// unavailable, so getEnvironment() resolves via the env-var / config fallback.)
|
|
120
|
+
// The core invariant: is*() DERIVE from getEnvironment(), so they can
|
|
121
|
+
// NEVER disagree with it, and exactly one is always true.
|
|
88
122
|
name: 'invariant: is*() exactly matches getEnvironment() + mutually exclusive (every scenario)',
|
|
89
123
|
run: (ctx) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const scenarios = [
|
|
94
|
-
{ env: { OMEGA_TEST_MODE: 'true', OMEGA_BUILD_MODE: 'true' }, expect: 'testing' },
|
|
95
|
-
{ env: { OMEGA_BUILD_MODE: 'true' }, expect: 'production' },
|
|
96
|
-
{ env: { NODE_ENV: 'development' }, expect: 'development' },
|
|
97
|
-
{ env: {}, expect: 'production' }, // @omega.js/desktop defaults prod (shipped artifact)
|
|
98
|
-
];
|
|
99
|
-
try {
|
|
100
|
-
for (const s of scenarios) {
|
|
101
|
-
delete process.env.OMEGA_TEST_MODE; delete process.env.OMEGA_BUILD_MODE; delete process.env.NODE_ENV;
|
|
102
|
-
for (const k of Object.keys(s.env)) process.env[k] = s.env[k];
|
|
124
|
+
withEnvironment(null, () => {
|
|
125
|
+
for (const name of ['development', 'testing', 'production']) {
|
|
126
|
+
process.env.OMEGA_ENVIRONMENT = name;
|
|
103
127
|
const e = Manager.getEnvironment();
|
|
104
|
-
ctx.expect(e).toBe(
|
|
128
|
+
ctx.expect(e).toBe(name);
|
|
105
129
|
ctx.expect(Manager.isDevelopment()).toBe(e === 'development');
|
|
106
130
|
ctx.expect(Manager.isTesting()).toBe(e === 'testing');
|
|
107
131
|
ctx.expect(Manager.isProduction()).toBe(e === 'production');
|
|
108
132
|
const trueCount = [Manager.isDevelopment(), Manager.isTesting(), Manager.isProduction()].filter(Boolean).length;
|
|
109
133
|
ctx.expect(trueCount).toBe(1);
|
|
110
134
|
}
|
|
111
|
-
}
|
|
112
|
-
if (prevTest === undefined) delete process.env.OMEGA_TEST_MODE; else process.env.OMEGA_TEST_MODE = prevTest;
|
|
113
|
-
if (prevBuild === undefined) delete process.env.OMEGA_BUILD_MODE; else process.env.OMEGA_BUILD_MODE = prevBuild;
|
|
114
|
-
if (prevNode === undefined) delete process.env.NODE_ENV; else process.env.NODE_ENV = prevNode;
|
|
115
|
-
}
|
|
135
|
+
});
|
|
116
136
|
},
|
|
117
137
|
},
|
|
118
138
|
{
|
|
@@ -162,13 +182,13 @@ module.exports = defineCases({
|
|
|
162
182
|
},
|
|
163
183
|
},
|
|
164
184
|
{
|
|
165
|
-
name: 'getWindowsSignStrategy reads config platforms.
|
|
185
|
+
name: 'getWindowsSignStrategy reads config platforms.windows.signing.strategy',
|
|
166
186
|
run: (ctx) => {
|
|
167
187
|
const fs = require('fs'); const os = require('os'); const path = require('path');
|
|
168
188
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-strategy-'));
|
|
169
189
|
fs.mkdirSync(path.join(tmp, 'config'), { recursive: true });
|
|
170
190
|
fs.writeFileSync(path.join(tmp, 'config', 'omega.json5'),
|
|
171
|
-
`{ targets: { desktop: { platforms: {
|
|
191
|
+
`{ targets: { desktop: { type: 'desktop', platforms: { windows: { signing: { strategy: 'cloud' } } } } } }`);
|
|
172
192
|
const orig = process.cwd();
|
|
173
193
|
try {
|
|
174
194
|
process.chdir(tmp);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Build-layer test: a library the runtime source requires by BARE specifier is
|
|
2
|
+
// DECLARED by this package ([#872](https://github.com/Omega-JS-Stack/omega/issues/872)).
|
|
3
|
+
//
|
|
4
|
+
// The class of bug: on a laptop the monorepo root hoists every workspace's deps
|
|
5
|
+
// into one node_modules, so `require('sharp')` resolves from packages/desktop even
|
|
6
|
+
// though the desktop manifest never named it. The packed tarball a runner installs
|
|
7
|
+
// carries only what the manifest declares, and the first mac CI publish died on
|
|
8
|
+
// `Cannot find module 'sharp'` inside resolve-icons.js. Nothing about that failure
|
|
9
|
+
// was visible locally, so the manifest is checked HERE instead.
|
|
10
|
+
//
|
|
11
|
+
// Scope: src/lib and src/gulp, the two trees that run unbundled from the installed
|
|
12
|
+
// package (main-process libs and the gulp build lane). Internal `@omega.js/*`
|
|
13
|
+
// packages are excluded: the prepare lane vendors them into dist/vendor and rewires
|
|
14
|
+
// the requires, so they are never resolved by name from an install.
|
|
15
|
+
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const { builtinModules } = require('module');
|
|
19
|
+
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
20
|
+
|
|
21
|
+
const PACKAGE_ROOT = path.join(__dirname, '..', '..', '..', '..');
|
|
22
|
+
const SOURCE_DIRS = ['lib', 'gulp'];
|
|
23
|
+
|
|
24
|
+
// The two specifiers that are ALLOWED to be undeclared, each one required inside a
|
|
25
|
+
// try/catch whose catch is the documented behaviour, not a crash:
|
|
26
|
+
// firebase @omega.js/client's own dependency; the client bridge falls back
|
|
27
|
+
// to no-op mode when it is not there (lib/client-bridge.js)
|
|
28
|
+
// app-builder-bin electron-builder's own dependency; blockmap generation is
|
|
29
|
+
// best-effort and warns-and-skips (lib/sign-helpers/update-info.js)
|
|
30
|
+
const GUARDED = new Set(['firebase', 'app-builder-bin']);
|
|
31
|
+
|
|
32
|
+
// Every bare specifier `require`d under a dir, as package names.
|
|
33
|
+
function requiredPackages(dir) {
|
|
34
|
+
const names = new Map();
|
|
35
|
+
|
|
36
|
+
const walk = (current) => {
|
|
37
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
38
|
+
const file = path.join(current, entry.name);
|
|
39
|
+
|
|
40
|
+
if (entry.isDirectory()) {
|
|
41
|
+
walk(file);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!entry.name.endsWith('.js')) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
49
|
+
for (const match of source.matchAll(/require\(\s*['"]([^'"]+)['"]\s*\)/g)) {
|
|
50
|
+
const specifier = match[1];
|
|
51
|
+
if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('node:')) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const name = specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0];
|
|
56
|
+
if (builtinModules.includes(name)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!names.has(name)) {
|
|
61
|
+
names.set(name, path.relative(PACKAGE_ROOT, file));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
walk(dir);
|
|
68
|
+
|
|
69
|
+
return names;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = defineCases({
|
|
73
|
+
type: 'suite',
|
|
74
|
+
layer: 'build',
|
|
75
|
+
description: 'manifest deps: what the runtime source requires, the package declares (#872)',
|
|
76
|
+
tests: [
|
|
77
|
+
{
|
|
78
|
+
name: 'every bare require under src/lib and src/gulp is declared by the manifest',
|
|
79
|
+
run: (ctx) => {
|
|
80
|
+
const pkg = require(path.join(PACKAGE_ROOT, 'package.json'));
|
|
81
|
+
// dependencies ship inside the tarball; peers and optionals are installed
|
|
82
|
+
// beside it (ensureTarget installs the peers on every verb).
|
|
83
|
+
const declared = new Set([
|
|
84
|
+
...Object.keys(pkg.dependencies || {}),
|
|
85
|
+
...Object.keys(pkg.peerDependencies || {}),
|
|
86
|
+
...Object.keys(pkg.optionalDependencies || {}),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const undeclared = [];
|
|
90
|
+
for (const dir of SOURCE_DIRS) {
|
|
91
|
+
for (const [name, file] of requiredPackages(path.join(PACKAGE_ROOT, 'src', dir))) {
|
|
92
|
+
// The package requiring ITSELF is node's self-reference through the
|
|
93
|
+
// exports map, and the internal packages are vendored at prepare time.
|
|
94
|
+
if (name === pkg.name || name.startsWith('@omega.js/') || GUARDED.has(name)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!declared.has(name)) {
|
|
98
|
+
undeclared.push(`${name} (${file})`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
ctx.expect(undeclared).toEqual([]);
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'sharp, the icon pipeline it was missing, is a dependency and not a hoisted accident',
|
|
108
|
+
run: (ctx) => {
|
|
109
|
+
const pkg = require(path.join(PACKAGE_ROOT, 'package.json'));
|
|
110
|
+
|
|
111
|
+
ctx.expect(Boolean(pkg.dependencies.sharp)).toBe(true);
|
|
112
|
+
ctx.expect(fs.readFileSync(path.join(PACKAGE_ROOT, 'src', 'lib', 'sign-helpers', 'resolve-icons.js'), 'utf8')).toContain("require('sharp')");
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
@@ -117,7 +117,11 @@ ${CUSTOM_MARKER}
|
|
|
117
117
|
},
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
|
-
|
|
120
|
+
// The framework block is COMPLETELY managed ([#926](https://github.com/Omega-JS-Stack/omega/issues/926)):
|
|
121
|
+
// a retired framework line and a line a user typed into the framework block
|
|
122
|
+
// cannot be told apart, and the block header says it is overwritten on
|
|
123
|
+
// every setup, so a line the new block does not carry leaves the file.
|
|
124
|
+
name: 'gitignore: a Default line the new defaults no longer carry DROPS instead of migrating (#926)',
|
|
121
125
|
run: (ctx) => {
|
|
122
126
|
const existing = `${DEFAULT_MARKER}
|
|
123
127
|
node_modules/
|
|
@@ -132,10 +136,29 @@ dist/
|
|
|
132
136
|
${CUSTOM_MARKER}
|
|
133
137
|
`;
|
|
134
138
|
const merged = mergeLineBasedFiles(existing, incoming, '.gitignore');
|
|
135
|
-
ctx.expect(merged).toContain('my-fork-only.txt');
|
|
139
|
+
ctx.expect(merged).not.toContain('my-fork-only.txt');
|
|
136
140
|
ctx.expect(merged).toContain('dist/');
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
// The value a retired key holds is the user's DATA, so it still migrates
|
|
145
|
+
// (the case above); an EMPTY retired key holds nothing and drops (#926).
|
|
146
|
+
name: 'env: an EMPTY retired key drops instead of migrating to Custom (#926)',
|
|
147
|
+
run: (ctx) => {
|
|
148
|
+
const existing = `${DEFAULT_MARKER}
|
|
149
|
+
GH_TOKEN="mine"
|
|
150
|
+
MY_EMPTY_KEY=
|
|
151
|
+
|
|
152
|
+
${CUSTOM_MARKER}
|
|
153
|
+
`;
|
|
154
|
+
const incoming = `${DEFAULT_MARKER}
|
|
155
|
+
GH_TOKEN=""
|
|
156
|
+
|
|
157
|
+
${CUSTOM_MARKER}
|
|
158
|
+
`;
|
|
159
|
+
const merged = mergeLineBasedFiles(existing, incoming, '.env');
|
|
160
|
+
ctx.expect(merged).not.toContain('MY_EMPTY_KEY');
|
|
161
|
+
ctx.expect(merged).toContain('GH_TOKEN="mine"');
|
|
139
162
|
},
|
|
140
163
|
},
|
|
141
164
|
{
|