@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
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* brand-version: the brand's ONE version
|
|
3
|
+
* ([#869](https://github.com/Omega-JS-Stack/omega/issues/869)).
|
|
4
|
+
*
|
|
5
|
+
* A brand root's `package.json` `version` is the master and every
|
|
6
|
+
* `targets/<name>/package.json` follows it. Targets keep a real `version`
|
|
7
|
+
* field because every tool reads it there (electron-builder, the extension
|
|
8
|
+
* manifest, npm), so nothing needs plumbing in dev or CI; what they never do
|
|
9
|
+
* is move on their own.
|
|
10
|
+
*
|
|
11
|
+
* Two halves, one rule:
|
|
12
|
+
* - `bumpBrandVersion` is the ONE writer, behind `omega bump` at the brand
|
|
13
|
+
* root. It never commits and never tags: the ship flow owns the
|
|
14
|
+
* `chore(release)` commit.
|
|
15
|
+
* - `assertBrandVersion` is the gate every framework's `omega deploy` runs
|
|
16
|
+
* before its secrets precheck, so a target that drifted is refused before
|
|
17
|
+
* anything is published or published to.
|
|
18
|
+
*
|
|
19
|
+
* Twin in shape of the manager's `assertFamilyVersions`
|
|
20
|
+
* ([#794](https://github.com/Omega-JS-Stack/omega/issues/794)), which refuses a
|
|
21
|
+
* mixed @omega.js FAMILY. Same verdict, different subject: that one is about
|
|
22
|
+
* the framework packages a brand installed, this one about the brand's own
|
|
23
|
+
* release number.
|
|
24
|
+
*/
|
|
25
|
+
const path = require('path');
|
|
26
|
+
const jetpack = require('fs-jetpack');
|
|
27
|
+
|
|
28
|
+
const { resolveBrandRoot } = require('../config/index.js');
|
|
29
|
+
const Logger = require('./logger.js');
|
|
30
|
+
|
|
31
|
+
const defaultLogger = new Logger('brand-version');
|
|
32
|
+
|
|
33
|
+
// What `omega bump` takes, in the order it prints them.
|
|
34
|
+
const KINDS = ['patch', 'minor', 'major'];
|
|
35
|
+
|
|
36
|
+
// A plain release number and nothing else: a prerelease tag is refused by name
|
|
37
|
+
// rather than guessed at (which digit does `patch` move on `1.0.0-beta.2`?).
|
|
38
|
+
const RELEASE = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A refusal: crafted text a human is meant to read, flagged so every surface
|
|
42
|
+
* prints it the same way (the message alone, nothing added), the same shape
|
|
43
|
+
* scaffold-guard's refusals take.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} message - The lines to print verbatim.
|
|
46
|
+
* @returns {Error} The flagged error to throw.
|
|
47
|
+
*/
|
|
48
|
+
function refusal(message) {
|
|
49
|
+
const error = new Error(message);
|
|
50
|
+
error.refusal = true;
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The brand's version, resolved from anywhere inside the brand.
|
|
56
|
+
*
|
|
57
|
+
* @param {object} options
|
|
58
|
+
* @param {string} options.dir - Any directory inside the brand (root or target).
|
|
59
|
+
* @returns {{ brandRoot: string, version: string }|null} null when `dir` is not inside a brand.
|
|
60
|
+
* @throws {Error} A refusal when the brand root carries no `version`: the
|
|
61
|
+
* master number cannot be defaulted, or every target would follow a guess.
|
|
62
|
+
*/
|
|
63
|
+
function readBrandVersion({ dir }) {
|
|
64
|
+
const brandRoot = resolveBrandRoot(dir);
|
|
65
|
+
if (!brandRoot) return null;
|
|
66
|
+
|
|
67
|
+
const manifest = path.join(brandRoot, 'package.json');
|
|
68
|
+
const version = (jetpack.read(manifest, 'json') || {}).version;
|
|
69
|
+
|
|
70
|
+
if (!version) {
|
|
71
|
+
throw refusal(
|
|
72
|
+
`${manifest} carries no "version", and the brand root is the one version every target follows (#869).\n`
|
|
73
|
+
+ ` fix: add "version" to ${manifest}, then run \`omega bump\` at the brand root`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { brandRoot, version };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Every target that carries a package.json, by name. A directory under
|
|
82
|
+
* `targets/` with no manifest is not a target (the manager's `discoverTargets`
|
|
83
|
+
* rule), and the list is sorted so a bump's output reads the same every run.
|
|
84
|
+
*
|
|
85
|
+
* @param {object} options
|
|
86
|
+
* @param {string} options.brandRoot - Brand monorepo root.
|
|
87
|
+
* @returns {Array<{ name: string, dir: string, manifest: string }>}
|
|
88
|
+
*/
|
|
89
|
+
function listTargetPackages({ brandRoot }) {
|
|
90
|
+
const targetsDir = path.join(brandRoot, 'targets');
|
|
91
|
+
|
|
92
|
+
return (jetpack.list(targetsDir) || [])
|
|
93
|
+
.filter((name) => !name.startsWith('.'))
|
|
94
|
+
.sort()
|
|
95
|
+
.map((name) => ({
|
|
96
|
+
name,
|
|
97
|
+
dir: path.join(targetsDir, name),
|
|
98
|
+
manifest: path.join(targetsDir, name, 'package.json'),
|
|
99
|
+
}))
|
|
100
|
+
.filter((entry) => jetpack.exists(entry.manifest) === 'file');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The next release number for a kind.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} previous - The brand's current version.
|
|
107
|
+
* @param {'patch'|'minor'|'major'} kind - Which digit moves.
|
|
108
|
+
* @returns {string} The next version.
|
|
109
|
+
* @throws {Error} A refusal when `previous` is not a plain release number.
|
|
110
|
+
*/
|
|
111
|
+
function nextVersion(previous, kind) {
|
|
112
|
+
const parts = RELEASE.exec(String(previous));
|
|
113
|
+
|
|
114
|
+
if (!parts) {
|
|
115
|
+
throw refusal(
|
|
116
|
+
`the brand version "${previous}" is not a plain major.minor.patch, and \`omega bump\` moves whole releases with no prerelease handling.\n`
|
|
117
|
+
+ ' fix: set a plain version in the brand root package.json first',
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const [major, minor, patch] = parts.slice(1).map(Number);
|
|
122
|
+
|
|
123
|
+
if (kind === 'major') return `${major + 1}.0.0`;
|
|
124
|
+
if (kind === 'minor') return `${major}.${minor + 1}.0`;
|
|
125
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The same content with `version` set, gaining the key after `name` when the
|
|
130
|
+
* file never had one, so a target's first bump reads like every sibling's
|
|
131
|
+
* manifest instead of appending a stray key at the end.
|
|
132
|
+
*
|
|
133
|
+
* @param {object} content - The parsed manifest (or lock entry).
|
|
134
|
+
* @param {string} next - The version to write.
|
|
135
|
+
* @returns {object} The content to write back.
|
|
136
|
+
*/
|
|
137
|
+
function withVersion(content, next) {
|
|
138
|
+
if (content.version !== undefined) return { ...content, version: next };
|
|
139
|
+
|
|
140
|
+
const updated = content.name === undefined ? { version: next } : {};
|
|
141
|
+
for (const [key, value] of Object.entries(content)) {
|
|
142
|
+
updated[key] = value;
|
|
143
|
+
if (key === 'name') updated.version = next;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return updated;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Write a JSON file the way npm writes them: 2-space indent, trailing newline.
|
|
151
|
+
*
|
|
152
|
+
* @param {string} file - Where to write.
|
|
153
|
+
* @param {object} content - What to write.
|
|
154
|
+
* @returns {void}
|
|
155
|
+
*/
|
|
156
|
+
function writeJson(file, content) {
|
|
157
|
+
jetpack.write(file, `${JSON.stringify(content, null, 2)}\n`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Move the brand and every target onto the next version. The ONE writer.
|
|
162
|
+
*
|
|
163
|
+
* Writes the brand root manifest, every target manifest (a target with no
|
|
164
|
+
* `version` GAINS one: every target follows), and the brand lock's entry for
|
|
165
|
+
* each target plus its own root version where the lock carries one, since a
|
|
166
|
+
* lock left behind would have `npm ci` install the old number.
|
|
167
|
+
*
|
|
168
|
+
* @param {object} options
|
|
169
|
+
* @param {string} options.brandRoot - Brand monorepo root.
|
|
170
|
+
* @param {'patch'|'minor'|'major'} options.kind - Which digit moves.
|
|
171
|
+
* @param {object} [options.logger] - Where the per-file lines go.
|
|
172
|
+
* @returns {{ previous: string, next: string, files: string[] }} Paths relative to the brand root.
|
|
173
|
+
* @throws {Error} A refusal on an unknown kind, a missing root version, or a prerelease one.
|
|
174
|
+
*/
|
|
175
|
+
function bumpBrandVersion({ brandRoot, kind, logger = defaultLogger }) {
|
|
176
|
+
if (!KINDS.includes(kind)) {
|
|
177
|
+
throw refusal(`\`omega bump\` takes ${KINDS.join(', ')}, and "${kind}" is none of them.`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const previous = readBrandVersion({ dir: brandRoot }).version;
|
|
181
|
+
const next = nextVersion(previous, kind);
|
|
182
|
+
const files = [];
|
|
183
|
+
|
|
184
|
+
// One line per file, always `<old> -> <new>`; a file that had no version
|
|
185
|
+
// says so rather than printing an empty left-hand side.
|
|
186
|
+
const write = (file, old) => {
|
|
187
|
+
const relative = path.relative(brandRoot, file);
|
|
188
|
+
files.push(relative);
|
|
189
|
+
logger.log(`${relative} ${old || '(none)'} -> ${next}`);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const rootManifest = path.join(brandRoot, 'package.json');
|
|
193
|
+
writeJson(rootManifest, withVersion(jetpack.read(rootManifest, 'json'), next));
|
|
194
|
+
write(rootManifest, previous);
|
|
195
|
+
|
|
196
|
+
const targets = listTargetPackages({ brandRoot });
|
|
197
|
+
|
|
198
|
+
for (const target of targets) {
|
|
199
|
+
const content = jetpack.read(target.manifest, 'json');
|
|
200
|
+
writeJson(target.manifest, withVersion(content, next));
|
|
201
|
+
write(target.manifest, content.version);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const lockPath = path.join(brandRoot, 'package-lock.json');
|
|
205
|
+
const lock = jetpack.exists(lockPath) === 'file' ? jetpack.read(lockPath, 'json') : null;
|
|
206
|
+
|
|
207
|
+
if (lock && lock.packages) {
|
|
208
|
+
for (const target of targets) {
|
|
209
|
+
const key = `targets/${target.name}`;
|
|
210
|
+
if (lock.packages[key]) lock.packages[key] = withVersion(lock.packages[key], next);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// The lock's own root number exists only when npm wrote one, and npm
|
|
214
|
+
// writes it from the root manifest: where it is absent, the next install
|
|
215
|
+
// is what puts it there.
|
|
216
|
+
if (lock.version) lock.version = next;
|
|
217
|
+
if (lock.packages[''] && lock.packages[''].version) lock.packages[''].version = next;
|
|
218
|
+
|
|
219
|
+
writeJson(lockPath, lock);
|
|
220
|
+
write(lockPath, previous);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { previous, next, files };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The deploy gate: refuse a target whose version has drifted from the brand's.
|
|
228
|
+
*
|
|
229
|
+
* Read-only, so every lane runs it (`--direct` and `--dry-run` included), and
|
|
230
|
+
* it runs BEFORE the secrets precheck: a drifted target must not push its
|
|
231
|
+
* secrets and dispatch a build of the wrong number. A match says nothing.
|
|
232
|
+
*
|
|
233
|
+
* @param {object} options
|
|
234
|
+
* @param {string} options.dir - The target directory being deployed.
|
|
235
|
+
* @returns {{ brandRoot: string, name: string, version: string }|null} null when there is nothing to compare.
|
|
236
|
+
* @throws {Error} A refusal naming the target and `omega bump`.
|
|
237
|
+
*/
|
|
238
|
+
function assertBrandVersion({ dir }) {
|
|
239
|
+
const brandRoot = resolveBrandRoot(dir);
|
|
240
|
+
if (!brandRoot) return null;
|
|
241
|
+
|
|
242
|
+
// Only a target follows the brand. A brand root, or any other directory
|
|
243
|
+
// inside the brand, has nothing of its own to compare.
|
|
244
|
+
const segments = path.relative(brandRoot, path.resolve(dir)).split(path.sep);
|
|
245
|
+
if (segments[0] !== 'targets' || !segments[1]) return null;
|
|
246
|
+
|
|
247
|
+
const name = segments[1];
|
|
248
|
+
const manifest = path.join(brandRoot, 'targets', name, 'package.json');
|
|
249
|
+
if (jetpack.exists(manifest) !== 'file') return null;
|
|
250
|
+
|
|
251
|
+
const found = (jetpack.read(manifest, 'json') || {}).version;
|
|
252
|
+
const { version } = readBrandVersion({ dir: brandRoot });
|
|
253
|
+
|
|
254
|
+
if (found !== version) {
|
|
255
|
+
throw refusal(
|
|
256
|
+
`targets/${name} ${found ? `is ${found}` : 'has no version'} but the brand is ${version}; `
|
|
257
|
+
+ 'run `omega bump` at the brand root (#869).',
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return { brandRoot, name, version };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = { readBrandVersion, listTargetPackages, bumpBrandVersion, assertBrandVersion, KINDS };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* build-json: the ONE composer and the ONE writer of `OMEGA_BUILD_JSON`
|
|
3
|
+
* ([#743](https://github.com/Omega-JS-Stack/omega/issues/743)).
|
|
4
|
+
*
|
|
5
|
+
* Every browser surface OMEGA builds (web pages and its service worker,
|
|
6
|
+
* desktop's renderer windows, every extension page and the extension's service
|
|
7
|
+
* worker) reads the same snapshot under the same name, so it is composed once
|
|
8
|
+
* here and delivered once: a single `build.js` at the artifact's web root that
|
|
9
|
+
* every shell loads with one script tag and every worker with one
|
|
10
|
+
* `importScripts` line. The banners and defines each framework grew for itself
|
|
11
|
+
* are gone with it (Ian 2026-09-12: "make it same shape and consumption
|
|
12
|
+
* everywhere as much as possible").
|
|
13
|
+
*
|
|
14
|
+
* Desktop's main and preload bundles are the one exception, and not to this
|
|
15
|
+
* file: they are NODE, they boot from the WHOLE resolved config inside the
|
|
16
|
+
* asar, and they keep carrying it in their own bundle.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const jetpack = require('fs-jetpack');
|
|
20
|
+
const { clientConfig } = require('../config/index.js');
|
|
21
|
+
|
|
22
|
+
// The wrapper's `mode`, one shape on every surface
|
|
23
|
+
// ([#894](https://github.com/Omega-JS-Stack/omega/issues/894)): the build's
|
|
24
|
+
// verdict (`environment`), whether it was a BUILD rather than a dev/watch run,
|
|
25
|
+
// and whether it publishes. A surface's own extra verdicts (desktop's
|
|
26
|
+
// `server`) stay inside that surface's Manager and never reach the artifact.
|
|
27
|
+
const MODE_KEYS = ['environment', 'build', 'publish'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compose the snapshot a browser artifact carries.
|
|
31
|
+
* @param {object} input
|
|
32
|
+
* @param {object} input.config - the target's resolved omega.json5.
|
|
33
|
+
* @param {object} input.pkg - the project package (`{ name, version, … }`), the wrapper's `package`.
|
|
34
|
+
* @param {object} input.mode - `{ environment, build, publish }`.
|
|
35
|
+
* @param {object} input.license - the build's license stamp (#320).
|
|
36
|
+
* @param {object} input.facts - the build facts that ride on the config (`runtime`, `environment`, `version`, `buildTime`, `target`, `dev`).
|
|
37
|
+
* @returns {object} `{ config, package, mode, license, builtAt }`.
|
|
38
|
+
* @throws {Error} when `mode` is missing one of its three keys: a half-stated verdict is a build bug, never a default.
|
|
39
|
+
*/
|
|
40
|
+
function composeBuildJson({ config, pkg, mode, license, facts }) {
|
|
41
|
+
const missing = MODE_KEYS.filter((key) => !(mode && key in mode));
|
|
42
|
+
if (missing.length) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`[@omega.js/devkit] OMEGA_BUILD_JSON needs a mode carrying ${MODE_KEYS.join(', ')}, missing: ${missing.join(', ')}.`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
config: clientConfig({ ...config, ...facts }),
|
|
50
|
+
package: pkg,
|
|
51
|
+
mode: { environment: mode.environment, build: mode.build, publish: mode.publish },
|
|
52
|
+
license,
|
|
53
|
+
builtAt: new Date().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The text of the `build.js` file every surface writes.
|
|
59
|
+
*
|
|
60
|
+
* TWO statements, the same two everywhere. `self` is the one global name that
|
|
61
|
+
* answers in a window and in a worker alike, so there is no IIFE and no
|
|
62
|
+
* `globalThis` dance to read past. The `dev` map is its OWN statement because
|
|
63
|
+
* `omega dev` REWRITES that line per request
|
|
64
|
+
* ([#346](https://github.com/Omega-JS-Stack/omega/issues/346)), so the file on
|
|
65
|
+
* disk stays the build's own snapshot while a served page gets the map of the
|
|
66
|
+
* stack running right now. A build with no local stack writes `null`, the
|
|
67
|
+
* value @omega.js/client already reads as "assume the classics".
|
|
68
|
+
* @param {object} buildJson - composeBuildJson()'s answer.
|
|
69
|
+
* @returns {string} the file text.
|
|
70
|
+
*/
|
|
71
|
+
function buildJsSource(buildJson) {
|
|
72
|
+
const { dev = null, ...config } = buildJson.config || {};
|
|
73
|
+
|
|
74
|
+
return `self.OMEGA_BUILD_JSON = ${JSON.stringify({ ...buildJson, config })};\n`
|
|
75
|
+
+ `self.OMEGA_BUILD_JSON.config.dev = ${JSON.stringify(dev)};\n`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Write `<webRoot>/build.js`.
|
|
80
|
+
* @param {string} webRoot - the artifact's web root (what `/` resolves to for its pages).
|
|
81
|
+
* @param {object} buildJson - composeBuildJson()'s answer.
|
|
82
|
+
* @returns {string} the path written.
|
|
83
|
+
*/
|
|
84
|
+
function writeBuildJs(webRoot, buildJson) {
|
|
85
|
+
const file = jetpack.path(webRoot, 'build.js');
|
|
86
|
+
jetpack.write(file, buildJsSource(buildJson));
|
|
87
|
+
|
|
88
|
+
return file;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = { composeBuildJson, buildJsSource, writeBuildJs, MODE_KEYS };
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* - the `@dev-only` strip (#18), registered for PRODUCTION builds only
|
|
14
14
|
* - minify / sourcemap by mode, and `bundle` + `metafile` on
|
|
15
15
|
* - one timing line per build, watch rebuilds included
|
|
16
|
+
* - `import.meta.url`, kept answering in CommonJS output (#906), so an
|
|
17
|
+
* ESM-only dependency runs bundled
|
|
16
18
|
*
|
|
17
19
|
* esbuild is REQUIRED LAZILY, from the CALLING FRAMEWORK's installation rather
|
|
18
20
|
* than devkit's. Devkit is vendored into each framework's dist at prepare time
|
|
@@ -52,6 +54,24 @@ const RESERVED_OPTIONS = ['bundle', 'metafile', 'minify', 'sourcemap', 'plugins'
|
|
|
52
54
|
// when it tries to shut down.
|
|
53
55
|
const MODES = ['build', 'watch'];
|
|
54
56
|
|
|
57
|
+
// The `import.meta.url` a CommonJS bundle keeps
|
|
58
|
+
// ([#906](https://github.com/Omega-JS-Stack/omega/issues/906)). esbuild rewrites
|
|
59
|
+
// `import.meta` to `{}` in `format: 'cjs'` output (its documented limitation:
|
|
60
|
+
// `import.meta` only exists in an ES module), so an ESM-only dependency that
|
|
61
|
+
// opens a require of its own with `createRequire(import.meta.url)`, which
|
|
62
|
+
// yargs-parser and a growing share of the registry do, is handed `undefined`
|
|
63
|
+
// and throws ERR_INVALID_ARG_VALUE the moment the app boots. Node's own answer
|
|
64
|
+
// for a CommonJS file is `pathToFileURL(__filename)`, so that is what the
|
|
65
|
+
// reference becomes.
|
|
66
|
+
//
|
|
67
|
+
// It is a define onto an IDENTIFIER plus a banner declaring it, rather than one
|
|
68
|
+
// define carrying the expression, because esbuild refuses an expression as a
|
|
69
|
+
// define value ("must be an entity name or JS literal"). The banner rides every
|
|
70
|
+
// emitted file, so each answers with its OWN path.
|
|
71
|
+
const IMPORT_META_URL = 'import.meta.url';
|
|
72
|
+
const IMPORT_META_URL_NAME = '__omegaImportMetaUrl';
|
|
73
|
+
const IMPORT_META_URL_SHIM = `var ${IMPORT_META_URL_NAME} = require('url').pathToFileURL(__filename).href;`;
|
|
74
|
+
|
|
55
75
|
/**
|
|
56
76
|
* esbuild as the CALLING FRAMEWORK resolves it (see the header).
|
|
57
77
|
* @param {string} frameworkRoot - the framework package's root (the dir holding its package.json)
|
|
@@ -130,6 +150,32 @@ function timingPlugin(destination, entryCount, watching) {
|
|
|
130
150
|
};
|
|
131
151
|
}
|
|
132
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Keep `import.meta.url` answering in a CommonJS bundle (see IMPORT_META_URL).
|
|
155
|
+
* Composed from the OUTPUT SHAPE rather than wired per caller: every cjs+node
|
|
156
|
+
* build has the hole, and no other output format has it (a browser bundle is
|
|
157
|
+
* iife or esm, and esm keeps `import.meta` natively).
|
|
158
|
+
* @param {object} config - the composed esbuild options, edited in place
|
|
159
|
+
* @throws {Error} when the caller defines the same key, the RESERVED_OPTIONS rule:
|
|
160
|
+
* two answers for one reference is a programmer error, not a merge
|
|
161
|
+
*/
|
|
162
|
+
function keepImportMetaUrl(config) {
|
|
163
|
+
if (config.format !== 'cjs' || config.platform !== 'node') return;
|
|
164
|
+
|
|
165
|
+
if (config.define && IMPORT_META_URL in config.define) {
|
|
166
|
+
throw new Error(`[devkit bundle] \`${IMPORT_META_URL}\` is composed by bundle() for CommonJS output and cannot be defined by a caller: a cjs+node build always gets \`pathToFileURL(__filename)\`, so a second answer would silently win or lose depending on merge order`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
config.define = { ...config.define, [IMPORT_META_URL]: IMPORT_META_URL_NAME };
|
|
170
|
+
// The shim is declared FIRST and the caller's banner rides below it,
|
|
171
|
+
// unchanged: anything the banner itself does could read the reference, and a
|
|
172
|
+
// declaration has to come before its readers.
|
|
173
|
+
config.banner = {
|
|
174
|
+
...config.banner,
|
|
175
|
+
js: [IMPORT_META_URL_SHIM, config.banner && config.banner.js].filter(Boolean).join('\n'),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
133
179
|
/**
|
|
134
180
|
* One esbuild message as a line: what, and where.
|
|
135
181
|
* @param {object} message - an esbuild warning or error
|
|
@@ -248,6 +294,8 @@ async function bundle(options) {
|
|
|
248
294
|
if (value != null) config[key] = value;
|
|
249
295
|
}
|
|
250
296
|
|
|
297
|
+
keepImportMetaUrl(config);
|
|
298
|
+
|
|
251
299
|
if (mode !== 'watch') {
|
|
252
300
|
return esbuild.build(config);
|
|
253
301
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a signing certificate has left: the ONE expiry reader
|
|
3
|
+
* ([#892](https://github.com/Omega-JS-Stack/omega/issues/892)).
|
|
4
|
+
*
|
|
5
|
+
* Two places used to parse `openssl x509 -enddate` with their own regex and
|
|
6
|
+
* their own date math (the manager's `validateManualCertificate`, the desktop's
|
|
7
|
+
* `validate-certs` provisioning-profile block), and neither could read a `.p12`
|
|
8
|
+
* at all, so the walk that PRODUCES the signing material never said how long it
|
|
9
|
+
* had left. One function answers for both container shapes:
|
|
10
|
+
*
|
|
11
|
+
* .cer / .pem openssl x509 -enddate (openssl 3 auto-detects DER/PEM)
|
|
12
|
+
* .p12 openssl pkcs12 -nokeys + x509 (the cert half, then the same read)
|
|
13
|
+
*
|
|
14
|
+
* The password rides an env var (`-passin env:`), never argv and never a shell
|
|
15
|
+
* string, exactly as the manager's export and the desktop's signing lookup pass
|
|
16
|
+
* theirs.
|
|
17
|
+
*
|
|
18
|
+
* It THROWS on a file it cannot read: an unreadable certificate is not "zero
|
|
19
|
+
* days left", it is a broken invariant the caller must report as its own error.
|
|
20
|
+
*/
|
|
21
|
+
const { execFileSync } = require('node:child_process');
|
|
22
|
+
const { extname } = require('node:path');
|
|
23
|
+
const jetpack = require('fs-jetpack');
|
|
24
|
+
|
|
25
|
+
const PASSWORD_ENV = 'OMEGA_P12_PASSWORD';
|
|
26
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
27
|
+
|
|
28
|
+
// The ONE renewal window: under this many days left, every reader of this
|
|
29
|
+
// module warns (the manager walk per type, the desktop's validate-certs step);
|
|
30
|
+
// past zero, every one of them errors.
|
|
31
|
+
const EXPIRY_WARN_DAYS = 30;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The certificate half of a .p12, as PEM.
|
|
35
|
+
*
|
|
36
|
+
* Two openssl families answer differently: OpenSSL 3 needs `-legacy` for the
|
|
37
|
+
* legacy-format containers the manager exports (macOS `security import` needs
|
|
38
|
+
* that format), while stock macOS /usr/bin/openssl is LibreSSL, which rejects
|
|
39
|
+
* the `-legacy` FLAG but reads legacy containers natively. A flag rejection is
|
|
40
|
+
* retried without it; anything else is the caller's error.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} filePath - Path to the .p12.
|
|
43
|
+
* @param {string} password - Password that opens it.
|
|
44
|
+
* @returns {string} PEM text.
|
|
45
|
+
*/
|
|
46
|
+
function p12Certificate(filePath, password) {
|
|
47
|
+
const run = (extra) => execFileSync(
|
|
48
|
+
'openssl',
|
|
49
|
+
['pkcs12', ...extra, '-in', filePath, '-nokeys', '-passin', `env:${PASSWORD_ENV}`],
|
|
50
|
+
{ encoding: 'utf8', stdio: 'pipe', env: { ...process.env, [PASSWORD_ENV]: password } },
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
return run(['-legacy']);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (String(error.stderr || '').includes('unknown option')) {
|
|
57
|
+
return run([]);
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* When a certificate expires, and how many days that is from now.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} filePath - A .cer, .pem or .p12 on disk.
|
|
67
|
+
* @param {object} [options]
|
|
68
|
+
* @param {string} [options.password] - The .p12 password (ignored for a .cer/.pem).
|
|
69
|
+
* @returns {{ expiresAt: Date, daysLeft: number }} `daysLeft` is negative for an
|
|
70
|
+
* expired certificate.
|
|
71
|
+
* @throws {Error} When the file is missing, unopenable, or carries no date.
|
|
72
|
+
*/
|
|
73
|
+
function certificateExpiry(filePath, { password = '' } = {}) {
|
|
74
|
+
if (!filePath || !jetpack.exists(filePath)) {
|
|
75
|
+
throw new Error(`certificateExpiry: no file at ${filePath}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const isP12 = extname(filePath).toLowerCase() === '.p12';
|
|
79
|
+
let output;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
output = isP12
|
|
83
|
+
? execFileSync('openssl', ['x509', '-enddate', '-noout'], {
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
stdio: 'pipe',
|
|
86
|
+
input: p12Certificate(filePath, password),
|
|
87
|
+
})
|
|
88
|
+
: execFileSync('openssl', ['x509', '-enddate', '-noout', '-in', filePath], {
|
|
89
|
+
encoding: 'utf8',
|
|
90
|
+
stdio: 'pipe',
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`certificateExpiry: could not read ${filePath}: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const match = String(output).match(/notAfter=(.+)/);
|
|
97
|
+
const expiresAt = match ? new Date(match[1].trim()) : null;
|
|
98
|
+
if (!expiresAt || Number.isNaN(expiresAt.getTime())) {
|
|
99
|
+
throw new Error(`certificateExpiry: no expiration date in ${filePath}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
expiresAt,
|
|
104
|
+
daysLeft: Math.floor((expiresAt.getTime() - Date.now()) / MS_PER_DAY),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { certificateExpiry, EXPIRY_WARN_DAYS };
|