@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
|
@@ -13,47 +13,92 @@ const path = require('node:path');
|
|
|
13
13
|
const { execSync, execFileSync } = require('node:child_process');
|
|
14
14
|
|
|
15
15
|
const { findBrandRoot, discoverTargets, frameworkPackagesOf } = require('./local.js');
|
|
16
|
+
const { stageLocalPackages, STAGING_DIR } = require('./pack-local.js');
|
|
17
|
+
const {
|
|
18
|
+
pushSnapshot,
|
|
19
|
+
pushWorkflowFiles,
|
|
20
|
+
composedWorkflowFiles,
|
|
21
|
+
defaultBranchOf,
|
|
22
|
+
healDefaultBranch,
|
|
23
|
+
waitForWorkflow,
|
|
24
|
+
waitForRef,
|
|
25
|
+
ghHeaders,
|
|
26
|
+
shortSha,
|
|
27
|
+
} = require('./deploy-snapshot.js');
|
|
28
|
+
// The remote-url parse this module published before the boot prelude needed it
|
|
29
|
+
// too (#890): it lives in the remote boundary now, re-exported here unchanged.
|
|
30
|
+
const { parseRemoteUrl } = require('./git-remote.js');
|
|
16
31
|
|
|
17
32
|
const API_BASE = 'https://api.github.com';
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function parseRemoteUrl(url) {
|
|
27
|
-
const match = (url || '').trim().match(/github\.com[/:]([^/]+)\/([^/\s]+?)(?:\.git)?$/);
|
|
28
|
-
if (!match) return null;
|
|
29
|
-
return { owner: match[1], repo: match[2] };
|
|
30
|
-
}
|
|
34
|
+
// The ONE branch a deploy ever builds from
|
|
35
|
+
// ([#915](https://github.com/Omega-JS-Stack/omega/issues/915)): every brand's
|
|
36
|
+
// snapshot lands here, nested or not, linked or not, and CI only ever runs what
|
|
37
|
+
// this branch holds. A brand's real history never carries packed tarballs, and
|
|
38
|
+
// whoever starts a deploy (a laptop, the admin publish) puts the files it needs
|
|
39
|
+
// on the same branch. Exported, so the backend's admin publish never types it.
|
|
40
|
+
const SNAPSHOT_REF = 'omega-deploy';
|
|
31
41
|
|
|
32
42
|
/**
|
|
33
|
-
* The repo a CI dispatch addresses: the
|
|
34
|
-
* ([#799](https://github.com/Omega-JS-Stack/omega/issues/799)
|
|
43
|
+
* The repo a CI dispatch addresses: the brand's SOURCE monorepo, from its
|
|
44
|
+
* config ([#799](https://github.com/Omega-JS-Stack/omega/issues/799),
|
|
45
|
+
* [#883](https://github.com/Omega-JS-Stack/omega/issues/883)). Every framework's
|
|
35
46
|
* deploy verb asks this instead of `resolveRepo` above, because a git remote
|
|
36
47
|
* answers the repo the working tree sits in: inside a brand monorepo nested in
|
|
37
48
|
* another repo (a brand inside the framework monorepo, a target checked out
|
|
38
49
|
* under someone else's tree) that is the ENCLOSING repo, so the dispatch went to
|
|
39
|
-
* a workflow that was never there.
|
|
50
|
+
* a workflow that was never there. The workflows live on the SOURCE repo in
|
|
51
|
+
* every case, whatever a target publishes to.
|
|
40
52
|
*
|
|
41
53
|
* Half an address addresses nothing, so it throws rather than POST to
|
|
42
54
|
* `undefined/<name>`.
|
|
43
55
|
*
|
|
44
56
|
* @param {object} config - Composed omega config for the target being deployed.
|
|
45
|
-
* @returns {{ owner: string, repo: string }} the
|
|
57
|
+
* @returns {{ owner: string, repo: string }} the source repo's owner and bare name
|
|
46
58
|
* @throws {Error} when the config names no repo
|
|
47
59
|
*/
|
|
48
60
|
function dispatchRepo(config) {
|
|
49
|
-
const {
|
|
50
|
-
const
|
|
61
|
+
const { sourceRepo } = require('../config/index.js');
|
|
62
|
+
const source = sourceRepo(config);
|
|
51
63
|
|
|
52
|
-
if (!
|
|
53
|
-
throw new Error('Could not determine the brand repo to dispatch on. Set repo.
|
|
64
|
+
if (!source) {
|
|
65
|
+
throw new Error('Could not determine the brand repo to dispatch on. Set repo.org (and brand.id) in config/omega.json5: the source repo is <brand.id>-omega under that org.');
|
|
54
66
|
}
|
|
55
67
|
|
|
56
|
-
return { owner, repo: name };
|
|
68
|
+
return { owner: source.owner, repo: source.name };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The whole CI dispatch ADDRESS for a target: the repo above, plus the workflow
|
|
73
|
+
* file the target's scaffold actually wrote
|
|
74
|
+
* ([#847](https://github.com/Omega-JS-Stack/omega/issues/847)). Inside a brand
|
|
75
|
+
* monorepo the target's CI lives in the BRAND ROOT's workflows dir under a
|
|
76
|
+
* per-target name (`desktop-build.yml`, #265), and standalone it keeps the
|
|
77
|
+
* framework's own name, so every deploy verb composed `dispatchRepo` with
|
|
78
|
+
* `composedWorkflowName` by hand and desktop kept a local helper its siblings
|
|
79
|
+
* lacked. One helper now, called by all four.
|
|
80
|
+
*
|
|
81
|
+
* @param {object} options
|
|
82
|
+
* @param {string} options.projectRoot - The target dir being deployed.
|
|
83
|
+
* @param {object} options.config - Composed omega config for that target.
|
|
84
|
+
* @param {string} options.workflow - The framework's workflow file name (e.g. publish.yml).
|
|
85
|
+
* @returns {{ owner: string, repo: string, workflow: string }} the repo and the workflow to dispatch
|
|
86
|
+
* @throws {Error} when the config names no repo
|
|
87
|
+
*/
|
|
88
|
+
function dispatchTarget({ projectRoot, config, workflow }) {
|
|
89
|
+
const { composedWorkflowName } = require('./ci-workflows.js');
|
|
90
|
+
const { resolveSeedMode } = require('../config/index.js');
|
|
91
|
+
const { owner, repo } = dispatchRepo(config);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
owner,
|
|
95
|
+
repo,
|
|
96
|
+
workflow: composedWorkflowName({
|
|
97
|
+
targetDir: projectRoot,
|
|
98
|
+
brandRoot: resolveSeedMode(projectRoot).brandRoot,
|
|
99
|
+
workflow,
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
57
102
|
}
|
|
58
103
|
|
|
59
104
|
/**
|
|
@@ -139,12 +184,7 @@ async function dispatchWorkflow(plan, options = {}) {
|
|
|
139
184
|
const fetchFn = options.fetchFn || fetch;
|
|
140
185
|
const response = await fetchFn(plan.url, {
|
|
141
186
|
method: plan.method,
|
|
142
|
-
headers:
|
|
143
|
-
Accept: 'application/vnd.github+json',
|
|
144
|
-
Authorization: `Bearer ${options.token}`,
|
|
145
|
-
'X-GitHub-Api-Version': '2022-11-28',
|
|
146
|
-
'User-Agent': 'omega-deploy',
|
|
147
|
-
},
|
|
187
|
+
headers: ghHeaders(options.token),
|
|
148
188
|
body: JSON.stringify(plan.body),
|
|
149
189
|
});
|
|
150
190
|
|
|
@@ -156,51 +196,316 @@ async function dispatchWorkflow(plan, options = {}) {
|
|
|
156
196
|
}
|
|
157
197
|
|
|
158
198
|
/**
|
|
159
|
-
* The
|
|
160
|
-
*
|
|
199
|
+
* The LANE half of the two lines a dispatch prints, one spelling for all four
|
|
200
|
+
* verbs: `<mode> lane, ref <ref>`, plus the snapshot sha when the brand root
|
|
201
|
+
* pushed one for this run ([#901](https://github.com/Omega-JS-Stack/omega/issues/901)),
|
|
202
|
+
* so a fan-out's log shows every target dispatching the SAME snapshot.
|
|
203
|
+
*
|
|
204
|
+
* @param {{ mode: string, ref: string }} lane - The resolved lane.
|
|
205
|
+
* @param {string} [snapshot] - The sha the run's snapshot sits at.
|
|
206
|
+
* @returns {string} the parenthesised label, without its parentheses
|
|
207
|
+
*/
|
|
208
|
+
function laneLabel(lane, snapshot) {
|
|
209
|
+
return `${lane.mode} lane, ref ${lane.ref}${snapshot ? ` @ ${shortSha(snapshot)}` : ''}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// The things a lane DOES, in one injectable seam (the `steps` shape
|
|
213
|
+
// `deploy-precheck` uses): the tests drive the order without a push, a pack or
|
|
214
|
+
// a network call, and the defaults are the real modules.
|
|
215
|
+
const LANE_STEPS = {
|
|
216
|
+
defaultBranch: defaultBranchOf,
|
|
217
|
+
heal: healDefaultBranch,
|
|
218
|
+
behind: assertNotBehind,
|
|
219
|
+
workflows: pushWorkflowFiles,
|
|
220
|
+
stage: stageLocalPackages,
|
|
221
|
+
push: pushSnapshot,
|
|
222
|
+
waitRef: waitForRef,
|
|
223
|
+
wait: waitForWorkflow,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The DELIVERY half of a lane: how this brand's code reaches GitHub before the
|
|
228
|
+
* dispatch that runs it. ONE implementation for its two callers
|
|
229
|
+
* ([#901](https://github.com/Omega-JS-Stack/omega/issues/901)): a target's own
|
|
230
|
+
* `deployViaDispatch` below, and the brand-root fan-out, which runs it once for
|
|
231
|
+
* the whole run rather than letting every target of a concurrent group perform
|
|
232
|
+
* it at the same brand root at the same moment.
|
|
233
|
+
*
|
|
234
|
+
* What the lane delivers, in order
|
|
235
|
+
* ([#915](https://github.com/Omega-JS-Stack/omega/issues/915)):
|
|
236
|
+
* 1. the DEFAULT branch read once, and healed when published output has taken
|
|
237
|
+
* it over ([#922](https://github.com/Omega-JS-Stack/omega/issues/922)): a
|
|
238
|
+
* `gh-pages` default moves back to `main` here, before step 2 writes a
|
|
239
|
+
* workflow file to a branch the next web deploy force-pushes over;
|
|
240
|
+
* 2. the BEHIND check, for a brand that is its own repo's toplevel: a checkout
|
|
241
|
+
* behind the remote default branch refuses, because the force-push below
|
|
242
|
+
* would overwrite the deploy branch with a tree that lacks what the admin
|
|
243
|
+
* publish committed there;
|
|
244
|
+
* 3. the composed WORKFLOW FILES to the default branch, and only when they
|
|
245
|
+
* differ, because GitHub registers a workflow from that branch alone. This
|
|
246
|
+
* is the one write a deploy makes outside the deploy branch: the local tree
|
|
247
|
+
* is never committed, and what the developer has not committed stays theirs;
|
|
248
|
+
* 4. the linked packages PACKED (tarballs are files in the tree, so they ride
|
|
249
|
+
* the snapshot like anything else untracked and not ignored), the brand
|
|
250
|
+
* folder force-pushed to the deploy branch, and the tree put back.
|
|
251
|
+
*
|
|
252
|
+
* A brand outside git (`repo: false`) delivers nothing at all: there is no
|
|
253
|
+
* index to snapshot from, so the dispatch runs on whatever the branch holds.
|
|
254
|
+
*
|
|
255
|
+
* @param {object} options - Options.
|
|
256
|
+
* @param {object} options.lane - The lane `resolveDeployLane` returned.
|
|
257
|
+
* @param {string} [options.owner] - The snapshot's repo owner (snapshot lane).
|
|
258
|
+
* @param {string} [options.repo] - The snapshot's repo name (snapshot lane).
|
|
259
|
+
* @param {string} [options.ref] - The ref the snapshot lands on (default: the lane's).
|
|
260
|
+
* @param {string} [options.token] - GitHub token for the snapshot push.
|
|
261
|
+
* @param {string} [options.message] - Snapshot commit message.
|
|
262
|
+
* @param {object} [options.logger] - Logger with `log` (silent when omitted).
|
|
263
|
+
* @param {function} [options.fetchFn] - Injectable fetch (the ref wait, tests).
|
|
264
|
+
* @param {object} [options.steps] - Lane step overrides (tests).
|
|
265
|
+
* @returns {Promise<{ sha: string|null }>} the sha the snapshot landed at, and
|
|
266
|
+
* null on a lane that pushes none
|
|
267
|
+
*/
|
|
268
|
+
async function deliverLane(options) {
|
|
269
|
+
const lane = options.lane;
|
|
270
|
+
const logger = options.logger;
|
|
271
|
+
const steps = { ...LANE_STEPS, ...(options.steps || {}) };
|
|
272
|
+
const ref = options.ref || lane.ref;
|
|
273
|
+
|
|
274
|
+
if (lane.mode !== 'snapshot') {
|
|
275
|
+
return { sha: null };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Read ONCE, used twice: the branch a behind checkout is measured against,
|
|
279
|
+
// and the branch the composed workflows are pushed to. Null is a repo GitHub
|
|
280
|
+
// does not have yet, and the snapshot push below is where that says itself.
|
|
281
|
+
// A dry run returns before this whole function, so it reads nothing here and
|
|
282
|
+
// heals nothing below ([#922](https://github.com/Omega-JS-Stack/omega/issues/922)).
|
|
283
|
+
const current = await steps.defaultBranch({
|
|
284
|
+
owner: options.owner,
|
|
285
|
+
repo: options.repo,
|
|
286
|
+
token: options.token,
|
|
287
|
+
fetchFn: options.fetchFn,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// The gh-pages HEAL, on the name just read and before anything is written to
|
|
291
|
+
// it (#922): published output is where the next web deploy's force-push wipes
|
|
292
|
+
// whatever the compose step put there, so the default branch moves back to
|
|
293
|
+
// main first and the rest of the lane uses the healed name.
|
|
294
|
+
const branch = await steps.heal({
|
|
295
|
+
owner: options.owner,
|
|
296
|
+
repo: options.repo,
|
|
297
|
+
current,
|
|
298
|
+
token: options.token,
|
|
299
|
+
fetchFn: options.fetchFn,
|
|
300
|
+
logger,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (branch) {
|
|
304
|
+
// A NESTED brand's git toplevel is somebody else's repo (this monorepo, for
|
|
305
|
+
// the playground), so there is no checkout of the brand's own repo to be
|
|
306
|
+
// behind: nothing to compare, and nothing to refuse.
|
|
307
|
+
if (!lane.nested) {
|
|
308
|
+
steps.behind({ cwd: lane.brandRoot, branch, logger });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
await steps.workflows({
|
|
312
|
+
brandRoot: lane.brandRoot,
|
|
313
|
+
owner: options.owner,
|
|
314
|
+
repo: options.repo,
|
|
315
|
+
branch,
|
|
316
|
+
token: options.token,
|
|
317
|
+
fetchFn: options.fetchFn,
|
|
318
|
+
logger,
|
|
319
|
+
});
|
|
320
|
+
} else if (logger) {
|
|
321
|
+
logger.log(`${options.owner}/${options.repo} answered no default branch (a repo nobody has created yet): no workflow compare, and the snapshot push speaks for itself.`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (logger) {
|
|
325
|
+
logger.log(`Snapshotting ${lane.brandRoot} to ${options.owner}/${options.repo}#${ref}${lane.linked ? ' (packing its linked packages)' : ''}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const staging = lane.linked
|
|
329
|
+
? await steps.stage({ dir: lane.brandRoot, log: logger ? (line) => logger.log(line) : undefined })
|
|
330
|
+
: null;
|
|
331
|
+
|
|
332
|
+
let sha;
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
sha = steps.push({
|
|
336
|
+
brandRoot: lane.brandRoot,
|
|
337
|
+
owner: options.owner,
|
|
338
|
+
repo: options.repo,
|
|
339
|
+
ref,
|
|
340
|
+
token: options.token,
|
|
341
|
+
message: options.message,
|
|
342
|
+
// What the stage just wrote has to reach the runner, so the push
|
|
343
|
+
// refuses a tree whose ignore rules would drop it.
|
|
344
|
+
require: staging ? [STAGING_DIR, 'package-lock.json'] : [],
|
|
345
|
+
});
|
|
346
|
+
} finally {
|
|
347
|
+
// ALWAYS, and as early as possible: the push carried the staged shape, so
|
|
348
|
+
// the developer's tree goes back before anything else can fail.
|
|
349
|
+
if (staging) {
|
|
350
|
+
await staging.restore();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Then the ref itself (#902): GitHub resolves the branch on its side, so a
|
|
355
|
+
// dispatch sent in the same second as the force-push can still resolve to the
|
|
356
|
+
// PREVIOUS commit and build a tree this deploy never pushed. Both callers get
|
|
357
|
+
// the wait from here, so every target of a run dispatches a settled ref.
|
|
358
|
+
await steps.waitRef({
|
|
359
|
+
owner: options.owner,
|
|
360
|
+
repo: options.repo,
|
|
361
|
+
ref,
|
|
362
|
+
sha,
|
|
363
|
+
token: options.token,
|
|
364
|
+
fetchFn: options.fetchFn,
|
|
365
|
+
logger,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
return { sha };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* The one deploy path: resolve repo + token, resolve the LANE, carry the code
|
|
373
|
+
* to GitHub the way that lane says, then dispatch. A dryRun returns the plan
|
|
374
|
+
* untouched instead.
|
|
375
|
+
*
|
|
376
|
+
* The lane runs only when the caller names a `dir` (a local tree). A
|
|
377
|
+
* server-side dispatch (the admin post route) has no working tree at all and
|
|
378
|
+
* addresses code GitHub already has, so it dispatches and nothing else.
|
|
379
|
+
*
|
|
161
380
|
* @param {object} options
|
|
162
381
|
* @param {string} options.workflow - workflow file name
|
|
382
|
+
* @param {string} [options.dir] - any dir inside the brand (turns the lane on)
|
|
163
383
|
* @param {string} [options.cwd] - repo dir for remote resolution
|
|
164
384
|
* @param {string} [options.owner] - explicit owner (skips git resolution)
|
|
165
385
|
* @param {string} [options.repo] - explicit repo (skips git resolution)
|
|
166
|
-
* @param {string} [options.ref] - branch (default main)
|
|
386
|
+
* @param {string} [options.ref] - branch (default: the lane's, else main)
|
|
167
387
|
* @param {object} [options.inputs] - workflow inputs
|
|
168
388
|
* @param {boolean} [options.dryRun] - build the plan but never send
|
|
389
|
+
* @param {string} [options.message] - snapshot commit message
|
|
390
|
+
* @param {string} [options.snapshot] - the sha a snapshot the CALLER already
|
|
391
|
+
* pushed to this lane's ref sits at (the brand-root fan-out's one push,
|
|
392
|
+
* [#901](https://github.com/Omega-JS-Stack/omega/issues/901)): the lane's
|
|
393
|
+
* delivery is done, so this run waits and dispatches against it
|
|
394
|
+
* @param {object} [options.logger] - logger with `log` (silent when omitted)
|
|
169
395
|
* @param {object} [options.env] - env map for token resolution
|
|
170
396
|
* @param {function} [options.fetchFn] - injectable fetch
|
|
171
397
|
* @param {function} [options.execFn] - injectable exec
|
|
172
|
-
* @
|
|
398
|
+
* @param {object} [options.steps] - lane step overrides (tests)
|
|
399
|
+
* @returns {Promise<{ plan: object, dispatched: boolean, lane: object|null, sha: string|null }>}
|
|
400
|
+
* the plan, whether it was sent, the lane, and the sha this deploy dispatched
|
|
401
|
+
* against (the caller's `snapshot`, else the one the lane pushed, else null on
|
|
402
|
+
* a lane that pushes none, [#902](https://github.com/Omega-JS-Stack/omega/issues/902))
|
|
173
403
|
*/
|
|
174
404
|
async function deployViaDispatch(options) {
|
|
175
405
|
const target = options.owner && options.repo
|
|
176
406
|
? { owner: options.owner, repo: options.repo }
|
|
177
407
|
: resolveRepo({ cwd: options.cwd, execFn: options.execFn });
|
|
178
408
|
|
|
409
|
+
const lane = options.dir ? resolveDeployLane({ dir: options.dir, execFn: options.execFn }) : null;
|
|
410
|
+
const logger = options.logger;
|
|
411
|
+
// What this deploy dispatches against, for the line it prints and for the
|
|
412
|
+
// follower's head check (#902): the caller's snapshot when it has one, else
|
|
413
|
+
// whatever the lane's own delivery pushed below.
|
|
414
|
+
let sha = options.snapshot || null;
|
|
415
|
+
|
|
416
|
+
// A sha only means something on the lane that pushes one (#901). A brand
|
|
417
|
+
// with no repo snapshots nothing, so a sha claiming to be on its ref is a
|
|
418
|
+
// ref this run never wrote: loud, at the boundary, before a step has run.
|
|
419
|
+
if (options.snapshot && !(lane && lane.mode === 'snapshot')) {
|
|
420
|
+
throw new Error(`--snapshot=${options.snapshot} is the snapshot lane's alone: this deploy takes the ${lane ? `${lane.mode} lane` : 'dispatch-only lane'}, which pushes no snapshot to skip.`);
|
|
421
|
+
}
|
|
422
|
+
|
|
179
423
|
const plan = buildDispatch({
|
|
180
424
|
owner: target.owner,
|
|
181
425
|
repo: target.repo,
|
|
182
426
|
workflow: options.workflow,
|
|
183
|
-
ref: options.ref,
|
|
427
|
+
ref: options.ref || (lane ? lane.ref : null),
|
|
184
428
|
inputs: options.inputs,
|
|
185
429
|
});
|
|
186
430
|
|
|
187
431
|
if (options.dryRun) {
|
|
188
|
-
|
|
432
|
+
// The plan IS the dry run, the lane included: what would happen, with
|
|
433
|
+
// neither git nor the network touched. The workflow half of it is read off
|
|
434
|
+
// DISK (the scaffold has already composed this run's files), so the preview
|
|
435
|
+
// names them and the branch they would go to without asking GitHub which of
|
|
436
|
+
// them differ (#915).
|
|
437
|
+
if (logger && lane && lane.mode === 'snapshot') {
|
|
438
|
+
const names = composedWorkflowFiles(lane.brandRoot).map((file) => file.name);
|
|
439
|
+
|
|
440
|
+
logger.log(names.length
|
|
441
|
+
? `Would compare ${names.join(', ')} with ${target.owner}/${target.repo}'s default branch and push the ones missing or changed there in one \`chore(ci): compose\` commit (a dry run reads nothing, so which differ is unknown here); the brand folder itself goes to ${plan.body.ref}.`
|
|
442
|
+
: `No composed workflow in ${lane.brandRoot}/.github/workflows: the default branch would receive nothing, and the brand folder goes to ${plan.body.ref}.`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return { plan, dispatched: false, lane, sha };
|
|
189
446
|
}
|
|
190
447
|
|
|
191
448
|
const token = options.token || resolveToken({ env: options.env, execFn: options.execFn });
|
|
449
|
+
const steps = { ...LANE_STEPS, ...(options.steps || {}) };
|
|
450
|
+
|
|
451
|
+
if (lane && lane.mode === 'snapshot' && options.snapshot) {
|
|
452
|
+
// The brand root pushed this run's snapshot ONCE, before it spawned a
|
|
453
|
+
// target (#901), so every step of the delivery is already done: the
|
|
454
|
+
// workflow compare, the packing and the push would only overwrite the very
|
|
455
|
+
// ref the other targets of this run are dispatching against. The wait and
|
|
456
|
+
// the dispatch below are the whole lane from here.
|
|
457
|
+
if (logger) {
|
|
458
|
+
logger.log(`Dispatching against the run's snapshot ${shortSha(options.snapshot)} on ${target.owner}/${target.repo}#${plan.body.ref} (the brand root pushed it once for every target)`);
|
|
459
|
+
}
|
|
460
|
+
} else if (lane) {
|
|
461
|
+
// Whatever this lane delivers, it is delivered in ONE place (#901), so a
|
|
462
|
+
// brand-root fan-out can run the very same delivery once for every target
|
|
463
|
+
// of a run instead of once per target.
|
|
464
|
+
const delivered = await deliverLane({
|
|
465
|
+
lane,
|
|
466
|
+
owner: target.owner,
|
|
467
|
+
repo: target.repo,
|
|
468
|
+
ref: plan.body.ref,
|
|
469
|
+
token,
|
|
470
|
+
message: options.message,
|
|
471
|
+
logger,
|
|
472
|
+
fetchFn: options.fetchFn,
|
|
473
|
+
steps,
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
sha = delivered.sha;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (lane) {
|
|
480
|
+
// Both lanes wait the same way: GitHub indexes a workflow it has just
|
|
481
|
+
// received a few seconds late, and a brand's FIRST deploy is exactly the
|
|
482
|
+
// push that carries the composed file. The wait reads the default branch,
|
|
483
|
+
// which is where a dispatch reads the workflow from.
|
|
484
|
+
await steps.wait({
|
|
485
|
+
owner: target.owner,
|
|
486
|
+
repo: target.repo,
|
|
487
|
+
workflow: options.workflow,
|
|
488
|
+
ref: plan.body.ref,
|
|
489
|
+
token,
|
|
490
|
+
fetchFn: options.fetchFn,
|
|
491
|
+
logger,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
192
495
|
await dispatchWorkflow(plan, { token, fetchFn: options.fetchFn });
|
|
193
|
-
return { plan, dispatched: true };
|
|
496
|
+
return { plan, dispatched: true, lane, sha };
|
|
194
497
|
}
|
|
195
498
|
|
|
196
499
|
/**
|
|
197
500
|
* Find every `file:` @omega.js spec in the brand tree. npm resolves the
|
|
198
501
|
* WHOLE workspace tree on any install, so one linked sibling target breaks a
|
|
199
502
|
* CI install even when the deploying target is clean (the cp194 lesson —
|
|
200
|
-
* linking is tree-wide, so detection is too).
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
503
|
+
* linking is tree-wide, so detection is too). `resolveDeployLane` reads it
|
|
504
|
+
* to pick the SNAPSHOT lane, which packs the linked frameworks so the runner
|
|
505
|
+
* installs the same code this tree runs
|
|
506
|
+
* ([#872](https://github.com/Omega-JS-Stack/omega/issues/872); this replaces
|
|
507
|
+
* the 2026-07-20 rule that a linked brand built and shipped the artifact from
|
|
508
|
+
* this machine, which is `--direct` now).
|
|
204
509
|
* @param {object} [options]
|
|
205
510
|
* @param {string} [options.dir] - Any directory inside the brand (default cwd).
|
|
206
511
|
* @returns {string[]} One line per offender: `<manifest> → <name>: <spec>`.
|
|
@@ -222,65 +527,130 @@ function findLocalSpecs(options = {}) {
|
|
|
222
527
|
}
|
|
223
528
|
|
|
224
529
|
/**
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
530
|
+
* The LANE a deploy takes, derived once for every target
|
|
531
|
+
* ([#872](https://github.com/Omega-JS-Stack/omega/issues/872)), and since
|
|
532
|
+
* [#915](https://github.com/Omega-JS-Stack/omega/issues/915) there is ONE of
|
|
533
|
+
* them: every brand with a git repo SNAPSHOTS its folder to `omega-deploy`, and
|
|
534
|
+
* CI only ever builds that branch, whoever started the deploy. The push lane
|
|
535
|
+
* (commit the developer's branch, push it, dispatch it) is gone, with the
|
|
536
|
+
* `git add -A` commit that swept work nobody meant to ship.
|
|
537
|
+
*
|
|
538
|
+
* Two facts still ride ON the lane, because the steps read them:
|
|
539
|
+
* - NESTED: the brand root is not the toplevel of the git repo it sits in (a
|
|
540
|
+
* brand inside this monorepo), so its git toplevel is somebody else's repo:
|
|
541
|
+
* the behind check has nothing to compare, and the secrets publisher skips
|
|
542
|
+
* its remote-mismatch guard.
|
|
543
|
+
* - LINKED: the brand tree carries a `file:` @omega.js spec anywhere. A runner
|
|
544
|
+
* can install none of those, so the packed tarballs have to travel with the
|
|
545
|
+
* snapshot.
|
|
546
|
+
*
|
|
547
|
+
* A brand outside git altogether (`repo` false) can snapshot nothing: its lane
|
|
548
|
+
* is the wait and the dispatch alone, on whatever the deploy branch holds.
|
|
549
|
+
*
|
|
550
|
+
* @param {object} [options] - Options.
|
|
228
551
|
* @param {string} [options.dir] - Any directory inside the brand (default cwd).
|
|
229
|
-
* @
|
|
552
|
+
* @param {function} [options.execFn] - Injectable exec (tests).
|
|
553
|
+
* @returns {{ mode: string, ref: string, nested: boolean, linked: boolean, repo: boolean, brandRoot: string }}
|
|
554
|
+
* The lane, plus the brand root every step of it works from.
|
|
230
555
|
*/
|
|
231
|
-
function
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
556
|
+
function resolveDeployLane(options = {}) {
|
|
557
|
+
const brandRoot = findBrandRoot(options.dir || process.cwd());
|
|
558
|
+
const execFn = options.execFn || ((cmd, opts) => execSync(cmd, opts).toString());
|
|
559
|
+
const git = (command) => {
|
|
560
|
+
try {
|
|
561
|
+
return String(execFn(command, { cwd: brandRoot, stdio: ['ignore', 'pipe', 'ignore'] })).trim();
|
|
562
|
+
} catch (e) {
|
|
563
|
+
// A brand outside git at all: there is nothing to snapshot and nothing to
|
|
564
|
+
// push, so the lane is the plain one and the deploy speaks for itself.
|
|
565
|
+
return '';
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const toplevel = git('git rev-parse --show-toplevel');
|
|
570
|
+
const repo = Boolean(toplevel);
|
|
571
|
+
const nested = repo && path.resolve(toplevel) !== path.resolve(brandRoot);
|
|
572
|
+
const linked = findLocalSpecs({ dir: brandRoot }).length > 0;
|
|
573
|
+
|
|
574
|
+
if (linked && !repo) {
|
|
575
|
+
// A linked brand has to SNAPSHOT (the packed tarballs travel no other way),
|
|
576
|
+
// and a snapshot is built out of a git index, so no repo is no lane. Said
|
|
577
|
+
// by name HERE, because the push would otherwise die a step later on a raw
|
|
578
|
+
// `fatal: not a git repository` out of `git check-ignore` (#872).
|
|
579
|
+
throw new Error(`${brandRoot} is not inside a git repo, and a linked brand needs a git repo to snapshot from: \`git init\` the brand, or restore registry versions with \`omega i live\`.`);
|
|
580
|
+
}
|
|
581
|
+
if (!repo) {
|
|
582
|
+
// Nothing to snapshot FROM, and nothing linked that would need to: the
|
|
583
|
+
// deploy is the wait and the dispatch, on whatever the branch already holds.
|
|
584
|
+
return { mode: 'dispatch', ref: SNAPSHOT_REF, nested, linked, repo, brandRoot };
|
|
239
585
|
}
|
|
586
|
+
|
|
587
|
+
return { mode: 'snapshot', ref: SNAPSHOT_REF, nested, linked, repo, brandRoot };
|
|
240
588
|
}
|
|
241
589
|
|
|
242
590
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
591
|
+
* Refuse a deploy from a checkout that is BEHIND the repo's default branch
|
|
592
|
+
* ([#915](https://github.com/Omega-JS-Stack/omega/issues/915)).
|
|
593
|
+
*
|
|
594
|
+
* The delivery force-pushes this folder over `omega-deploy`. A checkout missing
|
|
595
|
+
* commits the default branch already carries would therefore publish a site
|
|
596
|
+
* without them, and the admin publish is exactly that case: it commits a post
|
|
597
|
+
* to the default branch and to the deploy branch, and a stale laptop would
|
|
598
|
+
* force it back off both. So the deploy stops here, with the fix in the line.
|
|
599
|
+
*
|
|
600
|
+
* Plain git via argument arrays, like every other git call in this lane. No
|
|
601
|
+
* `origin` remote, or a remote that has no such branch yet, is nothing to be
|
|
602
|
+
* behind: both proceed.
|
|
603
|
+
*
|
|
604
|
+
* @param {object} options - Options.
|
|
605
|
+
* @param {string} options.cwd - The brand root (its own repo's toplevel).
|
|
606
|
+
* @param {string} options.branch - The repo's default branch.
|
|
607
|
+
* @param {object} [options.logger] - Logger with `log` (silent when omitted).
|
|
608
|
+
* @param {function} [options.execFn] - Injectable exec (tests).
|
|
609
|
+
* @throws {Error} When `origin/<branch>` is not an ancestor of HEAD.
|
|
251
610
|
*/
|
|
252
|
-
function
|
|
253
|
-
const cwd = options
|
|
254
|
-
const
|
|
611
|
+
function assertNotBehind(options) {
|
|
612
|
+
const { cwd, branch, logger } = options;
|
|
613
|
+
const execFn = options.execFn || ((args, opts) => execFileSync('git', args, opts));
|
|
614
|
+
const git = (args) => execFn(args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
255
615
|
|
|
256
|
-
execFileSync('git', ['add', '-A'], { cwd, stdio: 'inherit' });
|
|
257
|
-
|
|
258
|
-
// `git diff --cached --quiet` exits 1 exactly when something is staged
|
|
259
|
-
let hasStaged = false;
|
|
260
616
|
try {
|
|
261
|
-
|
|
617
|
+
git(['fetch', 'origin', branch]);
|
|
262
618
|
} catch (e) {
|
|
263
|
-
|
|
264
|
-
|
|
619
|
+
// ONLY the two answers that really mean there is nothing to compare
|
|
620
|
+
// against. A fetch that failed on the network, the token or the remote's
|
|
621
|
+
// permissions says nothing about this checkout, and proceeding on it would
|
|
622
|
+
// force-push a stale folder over everything the branch carries.
|
|
623
|
+
const said = `${e.stderr || ''}${e.message || ''}`;
|
|
265
624
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
625
|
+
if (!/couldn't find remote ref|does not appear to be a git repository|No such remote/i.test(said)) {
|
|
626
|
+
throw new Error(`Could not fetch origin/${branch} in ${cwd}, so there is no telling whether this checkout is behind it, and the deploy force-pushes the checkout over ${SNAPSHOT_REF}. git said: ${said.trim() || 'nothing at all'}`);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (logger) logger.log(`No origin/${branch} to compare against yet: nothing to be behind.`);
|
|
630
|
+
return;
|
|
270
631
|
}
|
|
271
632
|
|
|
272
|
-
|
|
633
|
+
try {
|
|
634
|
+
// Exits 0 exactly when the remote branch is already in this history.
|
|
635
|
+
git(['merge-base', '--is-ancestor', `origin/${branch}`, 'HEAD']);
|
|
636
|
+
} catch (e) {
|
|
637
|
+
throw new Error(`This checkout is behind origin/${branch}, and the deploy force-pushes it over ${SNAPSHOT_REF}: anything ${branch} carries that you do not (a post the admin published, a sibling's commit) would be deployed away. Run \`git pull\` in ${cwd}, then deploy again.`);
|
|
638
|
+
}
|
|
273
639
|
}
|
|
274
640
|
|
|
275
641
|
module.exports = {
|
|
276
642
|
parseRemoteUrl,
|
|
277
643
|
dispatchRepo,
|
|
644
|
+
dispatchTarget,
|
|
278
645
|
resolveRepo,
|
|
279
646
|
resolveToken,
|
|
280
647
|
buildDispatch,
|
|
281
648
|
dispatchWorkflow,
|
|
282
649
|
deployViaDispatch,
|
|
650
|
+
deliverLane,
|
|
651
|
+
laneLabel,
|
|
283
652
|
findLocalSpecs,
|
|
284
|
-
|
|
285
|
-
|
|
653
|
+
resolveDeployLane,
|
|
654
|
+
assertNotBehind,
|
|
655
|
+
SNAPSHOT_REF,
|
|
286
656
|
};
|