@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,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Firebase service-account key's ONE lookup
|
|
3
|
+
* ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)), and the ONE
|
|
4
|
+
* spelling of every path under the brand's secrets folder
|
|
5
|
+
* ([#897](https://github.com/Omega-JS-Stack/omega/issues/897)).
|
|
6
|
+
*
|
|
7
|
+
* The key is a FILE the cloud manage service mints, never a `.env` value, so
|
|
8
|
+
* everything that needs it (the backend stage, the deploy-roles read, the
|
|
9
|
+
* secrets publish that sends it to a runner) resolves the same two candidates
|
|
10
|
+
* in the same order: the target root's own, then the brand's secrets folder.
|
|
11
|
+
* It lived in the backend's stage helper while the backend was its only reader;
|
|
12
|
+
* the shared secrets publisher reads it now, and devkit never requires a
|
|
13
|
+
* framework, so the lookup lives here and the backend imports it.
|
|
14
|
+
*
|
|
15
|
+
* The Google OAuth client the authentication service writes obeys the same
|
|
16
|
+
* folder rule, so it is named here too: a rename of the folder is one edit,
|
|
17
|
+
* not a hunt through every manager and backend reader.
|
|
18
|
+
*/
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const jetpack = require('fs-jetpack');
|
|
21
|
+
const { findBrandRoot } = require('../config/index.js');
|
|
22
|
+
|
|
23
|
+
// The brand-level home of every minted credential, and the two files in it.
|
|
24
|
+
const SECRETS_DIR = path.join('.omega', 'secrets');
|
|
25
|
+
const SERVICE_ACCOUNT_REL = path.join(SECRETS_DIR, 'service-account.json');
|
|
26
|
+
const GOOGLE_OAUTH_REL = path.join(SECRETS_DIR, 'google-oauth.json');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The service-account key a target deploys with, or null when none is minted.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} projectDir - The target root.
|
|
32
|
+
* @returns {string|null} Absolute path to the key file.
|
|
33
|
+
*/
|
|
34
|
+
function resolveServiceAccountPath(projectDir) {
|
|
35
|
+
const brandRoot = findBrandRoot(projectDir);
|
|
36
|
+
|
|
37
|
+
return [
|
|
38
|
+
path.join(projectDir, 'service-account.json'),
|
|
39
|
+
brandRoot ? path.join(brandRoot, SERVICE_ACCOUNT_REL) : null,
|
|
40
|
+
].filter(Boolean).find((candidate) => jetpack.exists(candidate)) || null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { resolveServiceAccountPath, SECRETS_DIR, SERVICE_ACCOUNT_REL, GOOGLE_OAUTH_REL };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ship plan ([#867](https://github.com/Omega-JS-Stack/omega/issues/867)):
|
|
3
|
+
* what a brand's declaration says a target ships, what each shipped format is
|
|
4
|
+
* still missing, and the ONE wording both are reported in.
|
|
5
|
+
*
|
|
6
|
+
* @omega.js/config's platforms.js owns WHAT each format is and needs (the
|
|
7
|
+
* format table). This owns the two questions every lane asks of that table and
|
|
8
|
+
* the sentences the answers are printed in, so its three readers say the same
|
|
9
|
+
* thing in the same words:
|
|
10
|
+
*
|
|
11
|
+
* - the manage walk's `publishing` service, which collects what is missing;
|
|
12
|
+
* - the desktop publish, which refuses before it builds;
|
|
13
|
+
* - the extension publish, which refuses on a missing credential and prints
|
|
14
|
+
* the manual step for a listing only a human can create.
|
|
15
|
+
*
|
|
16
|
+
* Values are never read here, only NAMES: whether a key has a value is the
|
|
17
|
+
* whole question, and an empty string is absent (the .env cascade's own rule).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { enabledFormats, formatKeys, FORMATS } = require('../config/index.js');
|
|
21
|
+
|
|
22
|
+
// The ONE walk that collects every ship credential. Named in each refusal, so
|
|
23
|
+
// the fix is a command away instead of a hunt through the env schema.
|
|
24
|
+
const PUBLISHING_WALK = 'omega manage --service publishing';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Everything a target ships, each entry carrying what it needs.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} config - A resolved target config (the declaration is read
|
|
30
|
+
* at `config.platforms`, which is where `targets.<name>.platforms` resolves).
|
|
31
|
+
* @param {string} target - Target type ('desktop' | 'extension').
|
|
32
|
+
* @returns {Array<object>} In offer order: `{ platform, format, options, kind,
|
|
33
|
+
* ext, label, console, path, requires, listing }`. `path` is the declaration
|
|
34
|
+
* that enabled it, `requires` is narrowed to THIS brand, and `listing` names
|
|
35
|
+
* the config paths the format cannot ship without.
|
|
36
|
+
*/
|
|
37
|
+
function shipPlan(config, target) {
|
|
38
|
+
return enabledFormats(config, target).map((entry) => {
|
|
39
|
+
const spec = FORMATS[target][entry.platform][entry.format];
|
|
40
|
+
const { requires, listing } = formatKeys(target, entry.platform, entry.format, config);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
...entry,
|
|
44
|
+
kind: spec.kind,
|
|
45
|
+
...(spec.ext ? { ext: spec.ext } : {}),
|
|
46
|
+
...(spec.label ? { label: spec.label } : {}),
|
|
47
|
+
...(spec.console ? { console: spec.console } : {}),
|
|
48
|
+
path: `platforms.${entry.platform}.formats.${entry.format}`,
|
|
49
|
+
requires,
|
|
50
|
+
listing,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The keys a plan needs that the environment does not answer.
|
|
57
|
+
*
|
|
58
|
+
* @param {Array<object>} plan - A shipPlan.
|
|
59
|
+
* @param {object} [env] - Env map (default: process.env).
|
|
60
|
+
* @returns {Array<{ key: string, path: string, label: string }>} One entry per
|
|
61
|
+
* empty key, in declaration order; `path` is what requires it.
|
|
62
|
+
*/
|
|
63
|
+
function missingShipKeys(plan, env) {
|
|
64
|
+
const values = env || process.env;
|
|
65
|
+
const missing = [];
|
|
66
|
+
|
|
67
|
+
for (const entry of plan) {
|
|
68
|
+
for (const key of entry.requires) {
|
|
69
|
+
if (values[key]) continue;
|
|
70
|
+
missing.push({ key, path: entry.path, label: entry.label || entry.format });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return missing;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The refusal a missing ship credential earns: ONE line shape per key,
|
|
79
|
+
* `<KEY> (required by <path>): <the fix>`, the same shape push-secrets prints
|
|
80
|
+
* ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)), so the two
|
|
81
|
+
* rungs of the same story read alike.
|
|
82
|
+
*
|
|
83
|
+
* @param {Array<{ key, path, label }>} missing - missingShipKeys' return.
|
|
84
|
+
* @returns {string} The message (callers throw it, or return it as an error).
|
|
85
|
+
*/
|
|
86
|
+
function shipKeyRefusal(missing) {
|
|
87
|
+
const lines = missing.map(({ key, path, label }) => ` ${key} (required by ${path}): `
|
|
88
|
+
+ `${label} cannot publish without it. Run \`${PUBLISHING_WALK}\` (it asks for the key and writes the brand .env), then re-run.`);
|
|
89
|
+
|
|
90
|
+
return `${missing.length} ship credential(s) this brand's declaration requires are empty in the .env cascade (company/brand/target).\n${lines.join('\n')}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The step a missing listing id leaves a human. The store assigns the id when
|
|
95
|
+
* somebody creates the listing, so no lane can mint one: the publish attaches
|
|
96
|
+
* the artifact to the release and prints this, and the walk prints it when it
|
|
97
|
+
* had nobody to ask.
|
|
98
|
+
*
|
|
99
|
+
* @param {object} store
|
|
100
|
+
* @param {string} store.label - The store's name ('Chrome Web Store').
|
|
101
|
+
* @param {string} store.console - The page the listing is created on.
|
|
102
|
+
* @param {string} store.path - The brand-config path the id lands at.
|
|
103
|
+
* @param {string} [store.asset] - The release asset to upload, when a lane has one.
|
|
104
|
+
* @returns {string} One line.
|
|
105
|
+
*/
|
|
106
|
+
function listingManualStep({ label, console: consoleUrl, path, asset }) {
|
|
107
|
+
const upload = asset ? `, upload ${asset} from the release` : '';
|
|
108
|
+
|
|
109
|
+
return `${label} has no listing id yet: create the listing at ${consoleUrl}${upload}, then set ${path} in config/omega.json5 and re-run.`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = { shipPlan, missingShipKeys, shipKeyRefusal, listingManualStep, PUBLISHING_WALK };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signing paths, derived ONCE
|
|
3
|
+
* ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
|
|
4
|
+
*
|
|
5
|
+
* CSC_LINK and APPLE_API_KEY name FILES, and the files live in the signing
|
|
6
|
+
* tree: `<company or brand root>/.omega/certificates/apple/`, company tier
|
|
7
|
+
* first (`@omega.js/devkit/signing-tree`, #892). Nobody types those paths into
|
|
8
|
+
* a `.env`, so every reader used to derive them for itself: the build did, the
|
|
9
|
+
* secrets publish did, and validate-certs did NOT, which is how a deploy stopped
|
|
10
|
+
* on `Found .p12 in config/certs/ but CSC_LINK env var is not set` while the
|
|
11
|
+
* build beside it signed fine. One rule, one home: the desktop env load calls
|
|
12
|
+
* this, and the build, validate-certs and the secrets publish all read the env
|
|
13
|
+
* it populated.
|
|
14
|
+
*
|
|
15
|
+
* The lookup order this function IS (the four rungs the desktop signing lookup
|
|
16
|
+
* always had):
|
|
17
|
+
* 1. an explicit env value wins, always: nothing here overwrites a set key;
|
|
18
|
+
* 2. the COMPANY tree, when the brand names one (`company: { id }`, #677);
|
|
19
|
+
* 3. the BRAND's own tree;
|
|
20
|
+
* 4. nothing set, which leaves electron-builder's Keychain identity
|
|
21
|
+
* discovery as the default a tree-less machine keeps getting.
|
|
22
|
+
*
|
|
23
|
+
* A file that exists and cannot be USED is a PROBLEM, never a silent skip: a
|
|
24
|
+
* `.p12` the password does not open would fail the build outright, so it is
|
|
25
|
+
* reported and the key stays unset. Nothing here logs or throws; the caller
|
|
26
|
+
* speaks.
|
|
27
|
+
*
|
|
28
|
+
* There is no provisioning profile: Developer ID direct distribution needs
|
|
29
|
+
* none (the certificates walk requests none for those types, #891), so nothing
|
|
30
|
+
* derives one.
|
|
31
|
+
*/
|
|
32
|
+
const { execFileSync } = require('node:child_process');
|
|
33
|
+
const { join } = require('node:path');
|
|
34
|
+
const { findBrandRoot } = require('../config/index.js');
|
|
35
|
+
|
|
36
|
+
const { signingTree } = require('./signing-tree.js');
|
|
37
|
+
|
|
38
|
+
// Tree-relative paths, the ONE home of both: the certificates walk writes them
|
|
39
|
+
// here and every reader asks this module for them.
|
|
40
|
+
const CERT_REL = join('certificates', 'DEVELOPER_ID_APPLICATION_G2.p12');
|
|
41
|
+
const authKeyRel = (keyId) => `AuthKey_${keyId}.p8`;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Does this password actually open the container?
|
|
45
|
+
*
|
|
46
|
+
* Two openssl families answer differently: OpenSSL 3 needs `-legacy` to read
|
|
47
|
+
* the legacy-format containers the manager exports (macOS `security import`
|
|
48
|
+
* needs that format), while stock macOS /usr/bin/openssl is LibreSSL, which
|
|
49
|
+
* rejects the `-legacy` FLAG but reads legacy containers natively. A flag
|
|
50
|
+
* rejection is retried without it; only a real password/container failure
|
|
51
|
+
* counts as "does not open".
|
|
52
|
+
*
|
|
53
|
+
* @param {string} p12Path - Path to the .p12.
|
|
54
|
+
* @param {string} password - Candidate password.
|
|
55
|
+
* @returns {{ opens: boolean, tooling?: string }} - tooling set when openssl
|
|
56
|
+
* itself was unavailable/unusable, so the caller can say so instead of
|
|
57
|
+
* blaming the password.
|
|
58
|
+
*/
|
|
59
|
+
function opensWith(p12Path, password) {
|
|
60
|
+
// Password rides an env var, never argv, never a shell string.
|
|
61
|
+
const run = (args) => execFileSync(
|
|
62
|
+
'openssl',
|
|
63
|
+
['pkcs12', ...args, '-in', p12Path, '-nokeys', '-noout', '-passin', 'env:OMEGA_P12_PASSWORD'],
|
|
64
|
+
{ stdio: 'pipe', env: { ...process.env, OMEGA_P12_PASSWORD: password } },
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
run(['-legacy']);
|
|
69
|
+
return { opens: true };
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error.code === 'ENOENT') {
|
|
72
|
+
return { opens: false, tooling: 'openssl is not available' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (String(error.stderr || '').includes('unknown option')) {
|
|
76
|
+
try {
|
|
77
|
+
run([]);
|
|
78
|
+
return { opens: true };
|
|
79
|
+
} catch (retryError) {
|
|
80
|
+
if (retryError.code === 'ENOENT') {
|
|
81
|
+
return { opens: false, tooling: 'openssl is not available' };
|
|
82
|
+
}
|
|
83
|
+
return { opens: false };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { opens: false };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** An env value that is really set (an empty placeholder reads as unset). */
|
|
92
|
+
function isSet(value) {
|
|
93
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Derive the signing paths this target's tree implies. MUTATES `env`.
|
|
98
|
+
*
|
|
99
|
+
* @param {object} input
|
|
100
|
+
* @param {object} input.env - The env map to derive into (the process env).
|
|
101
|
+
* @param {string} input.targetDir - The target root; its brand names the tree.
|
|
102
|
+
* @returns {{ derived: Array<{ key: string, value: string }>, problems: Array<{ key: string, reason: string }> }}
|
|
103
|
+
* `derived` is what was written (absolute tree paths); `problems` are files
|
|
104
|
+
* that exist and cannot be used. Both empty outside a brand.
|
|
105
|
+
*/
|
|
106
|
+
function deriveSigningEnv({ env, targetDir }) {
|
|
107
|
+
const derived = [];
|
|
108
|
+
const problems = [];
|
|
109
|
+
|
|
110
|
+
// Outside a brand there is no tree, so there is nothing to derive.
|
|
111
|
+
const brandRoot = findBrandRoot(targetDir);
|
|
112
|
+
if (!brandRoot) {
|
|
113
|
+
return { derived, problems };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const tree = signingTree({ brandRoot });
|
|
117
|
+
|
|
118
|
+
// CSC_LINK: the Developer ID Application certificate.
|
|
119
|
+
if (!isSet(env.CSC_LINK)) {
|
|
120
|
+
const found = tree.find(CERT_REL);
|
|
121
|
+
|
|
122
|
+
if (found) {
|
|
123
|
+
if (!isSet(env.CSC_KEY_PASSWORD)) {
|
|
124
|
+
problems.push({ key: 'CSC_LINK', reason: `${found.path} is there but CSC_KEY_PASSWORD is unset, so it cannot be opened` });
|
|
125
|
+
} else {
|
|
126
|
+
const check = opensWith(found.path, env.CSC_KEY_PASSWORD);
|
|
127
|
+
|
|
128
|
+
if (check.tooling) {
|
|
129
|
+
problems.push({ key: 'CSC_LINK', reason: `${check.tooling}, so ${found.path} cannot be verified` });
|
|
130
|
+
} else if (!check.opens) {
|
|
131
|
+
problems.push({ key: 'CSC_LINK', reason: `CSC_KEY_PASSWORD does not open ${found.path}` });
|
|
132
|
+
} else {
|
|
133
|
+
env.CSC_LINK = found.path;
|
|
134
|
+
derived.push({ key: 'CSC_LINK', value: found.path });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// APPLE_API_KEY: the App Store Connect key, whose FILENAME carries the id,
|
|
141
|
+
// so an unset APPLE_API_KEY_ID means there is no path to look for.
|
|
142
|
+
if (!isSet(env.APPLE_API_KEY) && isSet(env.APPLE_API_KEY_ID)) {
|
|
143
|
+
const found = tree.find(authKeyRel(env.APPLE_API_KEY_ID.trim()));
|
|
144
|
+
|
|
145
|
+
if (found) {
|
|
146
|
+
env.APPLE_API_KEY = found.path;
|
|
147
|
+
derived.push({ key: 'APPLE_API_KEY', value: found.path });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { derived, problems };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Where each derivable key WOULD read from, whether or not the file is there:
|
|
156
|
+
* the line a refusal names instead of asking an operator to paste a path.
|
|
157
|
+
*
|
|
158
|
+
* @param {object} input
|
|
159
|
+
* @param {object} input.env - The env map (APPLE_API_KEY_ID names the AuthKey file).
|
|
160
|
+
* @param {string} input.targetDir - The target root.
|
|
161
|
+
* @returns {Array<{ key: string, paths: string[] }>} Absolute candidate paths,
|
|
162
|
+
* company tier first. Empty outside a brand.
|
|
163
|
+
*/
|
|
164
|
+
function signingPathCandidates({ env, targetDir }) {
|
|
165
|
+
const brandRoot = findBrandRoot(targetDir);
|
|
166
|
+
if (!brandRoot) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const { readDirs } = signingTree({ brandRoot });
|
|
171
|
+
|
|
172
|
+
return [
|
|
173
|
+
{ key: 'CSC_LINK', rel: CERT_REL },
|
|
174
|
+
{ key: 'APPLE_API_KEY', rel: isSet(env.APPLE_API_KEY_ID) ? authKeyRel(env.APPLE_API_KEY_ID.trim()) : null },
|
|
175
|
+
]
|
|
176
|
+
.filter((candidate) => candidate.rel)
|
|
177
|
+
.map(({ key, rel }) => ({ key, paths: readDirs.map((dir) => join(dir, rel)) }));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = { deriveSigningEnv, signingPathCandidates, opensWith, CERT_REL };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signing tree, in TWO tiers: the ONE resolution of where Apple signing
|
|
3
|
+
* material is read from and written to
|
|
4
|
+
* ([#892](https://github.com/Omega-JS-Stack/omega/issues/892)).
|
|
5
|
+
*
|
|
6
|
+
* ONE Apple account signs everything a company ships, so a brand that names a
|
|
7
|
+
* company ([#677](https://github.com/Omega-JS-Stack/omega/issues/677)) REUSES
|
|
8
|
+
* that company's material instead of minting its own:
|
|
9
|
+
*
|
|
10
|
+
* READ company tree first (`<companyRoot>/.omega/certificates/apple/`),
|
|
11
|
+
* then the brand's own. The tier a file came from is part of the
|
|
12
|
+
* answer, so a walk can say "reused from company".
|
|
13
|
+
* WRITE the company tree when there is one, the brand tree otherwise. New
|
|
14
|
+
* material a company-managed brand produces belongs to the company.
|
|
15
|
+
*
|
|
16
|
+
* Every consumer resolves through this one function: the manager's certificates
|
|
17
|
+
* walk, the manager's disperse writer, and the desktop verbs' own delivery (the
|
|
18
|
+
* `omega deploy --local` lane), which has no manager context and reads the
|
|
19
|
+
* company root through @omega.js/config's resolver instead.
|
|
20
|
+
*/
|
|
21
|
+
const { join } = require('node:path');
|
|
22
|
+
const jetpack = require('fs-jetpack');
|
|
23
|
+
const { resolveCompany } = require('../config/index.js');
|
|
24
|
+
|
|
25
|
+
const APPLE_REL = join('.omega', 'certificates', 'apple');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The signing tree a root holds: the ONE home of that path.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} root - Company tree root or brand root.
|
|
31
|
+
* @returns {string} Absolute path to .omega/certificates/apple/.
|
|
32
|
+
*/
|
|
33
|
+
function certsSourceDir(root) {
|
|
34
|
+
return join(root, APPLE_REL);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The company TREE a brand inherits from (`<parent root>/company`), or null.
|
|
39
|
+
* The manager hands its own `context.companyRoot`; everything else asks here.
|
|
40
|
+
*
|
|
41
|
+
* @param {string|null} brandRoot - The brand root.
|
|
42
|
+
* @returns {string|null}
|
|
43
|
+
*/
|
|
44
|
+
function companyTree(brandRoot) {
|
|
45
|
+
return brandRoot ? (resolveCompany(brandRoot).dir || null) : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a brand's signing tree.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} input
|
|
52
|
+
* @param {string|null} input.brandRoot - The brand root (null outside a brand).
|
|
53
|
+
* @param {string|null} [input.companyRoot] - The company TREE, when the caller
|
|
54
|
+
* already resolved it (the manager's context does). Pass nothing and it is
|
|
55
|
+
* resolved from the brand root.
|
|
56
|
+
* @returns {{ writeDir: string|null, readDirs: string[], companyDir: string|null,
|
|
57
|
+
* brandDir: string|null, find: Function, path: Function }}
|
|
58
|
+
* `find(rel)` answers `{ path, source }` for the first tier holding that
|
|
59
|
+
* relative path (company first), null when neither does; `path(rel)` is where
|
|
60
|
+
* a WRITE of it goes.
|
|
61
|
+
*/
|
|
62
|
+
function signingTree({ brandRoot = null, companyRoot } = {}) {
|
|
63
|
+
const company = companyRoot === undefined ? companyTree(brandRoot) : companyRoot;
|
|
64
|
+
const companyDir = company ? certsSourceDir(company) : null;
|
|
65
|
+
const brandDir = brandRoot ? certsSourceDir(brandRoot) : null;
|
|
66
|
+
|
|
67
|
+
// Company FIRST, always: a brand of a company reuses its material.
|
|
68
|
+
const tiers = [
|
|
69
|
+
...(companyDir ? [{ dir: companyDir, source: 'company' }] : []),
|
|
70
|
+
...(brandDir ? [{ dir: brandDir, source: 'brand' }] : []),
|
|
71
|
+
];
|
|
72
|
+
const writeDir = companyDir || brandDir;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
writeDir,
|
|
76
|
+
readDirs: tiers.map((tier) => tier.dir),
|
|
77
|
+
companyDir,
|
|
78
|
+
brandDir,
|
|
79
|
+
find: (rel) => {
|
|
80
|
+
for (const tier of tiers) {
|
|
81
|
+
const candidate = join(tier.dir, rel);
|
|
82
|
+
if (jetpack.exists(candidate)) {
|
|
83
|
+
return { path: candidate, source: tier.source };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
},
|
|
88
|
+
path: (rel) => (writeDir ? join(writeDir, rel) : null),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { signingTree, certsSourceDir, companyTree, APPLE_REL };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-target shape differences on the ONE secrets transport
|
|
3
|
+
* ([#891](https://github.com/Omega-JS-Stack/omega/issues/891)).
|
|
4
|
+
*
|
|
5
|
+
* `publishTargetSecrets` is the same call for every target; what differs is
|
|
6
|
+
* three small things, and they live here rather than in four framework bind
|
|
7
|
+
* files that each had to remember the contract:
|
|
8
|
+
*
|
|
9
|
+
* resolveValue applied to each collected value before it travels
|
|
10
|
+
* deriveValues applied to the COMPOSED map before anything reads it, so a
|
|
11
|
+
* key the brand never typed can still be valued (and a key it
|
|
12
|
+
* could NOT value names its own fix line)
|
|
13
|
+
* extraSecrets already-valued keys with no `.env` home at all
|
|
14
|
+
*
|
|
15
|
+
* devkit never requires a framework package, so nothing here imports desktop or
|
|
16
|
+
* backend: the desktop derivation is devkit's own signing-env module (the same
|
|
17
|
+
* one the desktop env load calls), and the backend credential is devkit's own
|
|
18
|
+
* service-account lookup.
|
|
19
|
+
*/
|
|
20
|
+
const path = require('node:path');
|
|
21
|
+
const fs = require('node:fs');
|
|
22
|
+
const jetpack = require('fs-jetpack');
|
|
23
|
+
const { findBrandRoot } = require('../config/index.js');
|
|
24
|
+
|
|
25
|
+
const { deriveSigningEnv, signingPathCandidates } = require('./signing-env.js');
|
|
26
|
+
const { resolveServiceAccountPath } = require('./service-account.js');
|
|
27
|
+
|
|
28
|
+
// The env schema's name for the backend deploy credential
|
|
29
|
+
// (`delivery: { backend: 'ci' }`): the service-account JSON itself, which the
|
|
30
|
+
// workflow writes back to disk.
|
|
31
|
+
const SERVICE_ACCOUNT_KEY = 'OMEGA_SERVICE_ACCOUNT_JSON';
|
|
32
|
+
|
|
33
|
+
// The walk that PRODUCES the signing material a derivation could not find.
|
|
34
|
+
const CERTIFICATES_WALK = 'omega manage --service certificates';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Desktop's value seam: base64 for a value that names an existing file, the
|
|
38
|
+
* value itself otherwise.
|
|
39
|
+
*
|
|
40
|
+
* The derived signing paths are ABSOLUTE (they point into the signing tree,
|
|
41
|
+
* which lives outside the target), so an absolute value is resolved as given.
|
|
42
|
+
* A RELATIVE one still resolves under the target root then the brand root,
|
|
43
|
+
* because an operator may still type `CSC_LINK=config/certs/mine.p12` by hand.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} options
|
|
46
|
+
* @param {string} options.targetDir - The target root.
|
|
47
|
+
* @returns {function} `(value, key) => string`
|
|
48
|
+
*/
|
|
49
|
+
function fileValueResolver({ targetDir }) {
|
|
50
|
+
const brandRoot = findBrandRoot(targetDir);
|
|
51
|
+
|
|
52
|
+
return (value) => fileContentsBase64(value, { targetDir, brandRoot }) || value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The base64 contents of the file `value` names, or null when it names none.
|
|
56
|
+
// Heuristic: a path-shaped value (a separator, or a typical cert/key extension)
|
|
57
|
+
// that resolves to a real file. A path-shaped value that exists nowhere is
|
|
58
|
+
// pushed as-is: it is a string.
|
|
59
|
+
function fileContentsBase64(value, { targetDir, brandRoot }) {
|
|
60
|
+
const looksLikePath = /[/\\]/.test(value) || /\.(p12|pem|cer|p8|provisionprofile|crt|key|json)$/i.test(value);
|
|
61
|
+
if (!looksLikePath) return null;
|
|
62
|
+
|
|
63
|
+
const roots = path.isAbsolute(value) ? [''] : [targetDir, brandRoot].filter(Boolean);
|
|
64
|
+
for (const root of roots) {
|
|
65
|
+
const absolute = path.isAbsolute(value) ? value : path.join(root, value);
|
|
66
|
+
if (fs.existsSync(absolute) && fs.statSync(absolute).isFile()) {
|
|
67
|
+
return fs.readFileSync(absolute).toString('base64');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Desktop's COMPOSED-map seam: the signing paths the brand's signing tree
|
|
76
|
+
* holds, by the SAME derivation the desktop env load runs, plus the fix line
|
|
77
|
+
* each key the tree could not answer should carry.
|
|
78
|
+
*
|
|
79
|
+
* @param {object} options
|
|
80
|
+
* @param {string} options.targetDir - The target root.
|
|
81
|
+
* @returns {function} `(values) => { derived, fixes }`
|
|
82
|
+
*/
|
|
83
|
+
function signingValueDeriver({ targetDir }) {
|
|
84
|
+
return (values) => {
|
|
85
|
+
const { derived, problems } = deriveSigningEnv({ env: values, targetDir });
|
|
86
|
+
const problemFor = new Map(problems.map(({ key, reason }) => [key, reason]));
|
|
87
|
+
|
|
88
|
+
const fixes = {};
|
|
89
|
+
for (const { key, paths } of signingPathCandidates({ env: values, targetDir })) {
|
|
90
|
+
if (values[key]) continue;
|
|
91
|
+
|
|
92
|
+
fixes[key] = problemFor.has(key)
|
|
93
|
+
? `${problemFor.get(key)}. Fix it, then re-run.`
|
|
94
|
+
: `no signing file at ${paths.join(' or ')}. Run \`${CERTIFICATES_WALK}\` to produce it, then re-run.`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { derived, fixes };
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The backend's service-account secret, valued from the authored key chain. An
|
|
103
|
+
* absent key publishes nothing: a brand that has not minted one yet deploys
|
|
104
|
+
* from a runner that cannot authenticate, and the missing secret is what says
|
|
105
|
+
* so.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} targetDir - The target root.
|
|
108
|
+
* @returns {Object<string, string>} The one-key map, or an empty one.
|
|
109
|
+
*/
|
|
110
|
+
function serviceAccountSecret(targetDir) {
|
|
111
|
+
const keyPath = resolveServiceAccountPath(targetDir);
|
|
112
|
+
const contents = keyPath ? jetpack.read(keyPath) : null;
|
|
113
|
+
|
|
114
|
+
return contents ? { [SERVICE_ACCOUNT_KEY]: contents } : {};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Target type to its seams. A type with no entry has none, which is web and the
|
|
118
|
+
// extension: plain string secrets, nothing to reshape.
|
|
119
|
+
const SEAMS = {
|
|
120
|
+
desktop: ({ targetDir }) => ({
|
|
121
|
+
resolveValue: fileValueResolver({ targetDir }),
|
|
122
|
+
deriveValues: signingValueDeriver({ targetDir }),
|
|
123
|
+
}),
|
|
124
|
+
backend: ({ targetDir }) => ({
|
|
125
|
+
extraSecrets: serviceAccountSecret(targetDir),
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The seams this target type brings to the publish.
|
|
131
|
+
*
|
|
132
|
+
* @param {object} input
|
|
133
|
+
* @param {string} input.target - Target type ('desktop', 'backend', 'web', 'extension').
|
|
134
|
+
* @param {string} input.targetDir - The target root.
|
|
135
|
+
* @returns {{ resolveValue?: function, deriveValues?: function, extraSecrets?: object }}
|
|
136
|
+
*/
|
|
137
|
+
function targetSeams({ target, targetDir }) {
|
|
138
|
+
const build = SEAMS[target];
|
|
139
|
+
return build ? build({ targetDir }) : {};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = { targetSeams, fileValueResolver, signingValueDeriver, serviceAccountSecret, SERVICE_ACCOUNT_KEY };
|