@fairgarden/distribution 0.1.0-alpha.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 +94 -0
- package/dist/add-module.d.ts +58 -0
- package/dist/add-module.d.ts.map +1 -0
- package/dist/add-module.js +250 -0
- package/dist/add-module.js.map +1 -0
- package/dist/canary.d.ts +58 -0
- package/dist/canary.d.ts.map +1 -0
- package/dist/canary.js +117 -0
- package/dist/canary.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +747 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-edit.d.ts +18 -0
- package/dist/config-edit.d.ts.map +1 -0
- package/dist/config-edit.js +166 -0
- package/dist/config-edit.js.map +1 -0
- package/dist/extends.d.ts +59 -0
- package/dist/extends.d.ts.map +1 -0
- package/dist/extends.js +163 -0
- package/dist/extends.js.map +1 -0
- package/dist/extract.d.ts +31 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +177 -0
- package/dist/extract.js.map +1 -0
- package/dist/git-url.d.ts +19 -0
- package/dist/git-url.d.ts.map +1 -0
- package/dist/git-url.js +55 -0
- package/dist/git-url.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/overrides.d.ts +32 -0
- package/dist/overrides.d.ts.map +1 -0
- package/dist/overrides.js +81 -0
- package/dist/overrides.js.map +1 -0
- package/dist/readme.d.ts +58 -0
- package/dist/readme.d.ts.map +1 -0
- package/dist/readme.js +299 -0
- package/dist/readme.js.map +1 -0
- package/dist/release.d.ts +140 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +409 -0
- package/dist/release.js.map +1 -0
- package/dist/scaffold.d.ts +46 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +389 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/submodules.d.ts +95 -0
- package/dist/submodules.d.ts.map +1 -0
- package/dist/submodules.js +225 -0
- package/dist/submodules.js.map +1 -0
- package/dist/workflows.d.ts +57 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +358 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +51 -0
package/Readme.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# FairGarden Distribution
|
|
2
|
+
|
|
3
|
+
<!-- fg:version -->
|
|
4
|
+
|
|
5
|
+
Version **0.1.0-alpha.0**
|
|
6
|
+
|
|
7
|
+
<!-- /fg:version -->
|
|
8
|
+
|
|
9
|
+
<!-- fg:releasing -->
|
|
10
|
+
|
|
11
|
+
## Releasing
|
|
12
|
+
|
|
13
|
+
This module releases on its own. `0.1.0-alpha.0` is what main is working towards,
|
|
14
|
+
not what is published — the version here is always the next one.
|
|
15
|
+
|
|
16
|
+
1. **Publish it.** Run the *Publish* workflow from the Actions tab, picking the
|
|
17
|
+
dist tag. It refuses if that version is already on npm.
|
|
18
|
+
2. **Move it on.** `pnpm release` — opens a pull request bumping this branch
|
|
19
|
+
to `0.1.0-alpha.1`, or `pnpm release --id rc` to change
|
|
20
|
+
identifier. A prerelease gets no maintenance branch; there is no released
|
|
21
|
+
line behind it yet.
|
|
22
|
+
|
|
23
|
+
Every push to main publishes `@fairgarden/distribution@canary`. A canary is not a release and
|
|
24
|
+
carries no promise; it is there so main can be tried without a checkout.
|
|
25
|
+
|
|
26
|
+
<!-- /fg:releasing -->
|
|
27
|
+
|
|
28
|
+
Ship a set of versioned modules together.
|
|
29
|
+
|
|
30
|
+
End users do not care about semver — they want to know how old their copy is,
|
|
31
|
+
and `2024.12.01` says that at a glance where `1.2.1` does not. Developers do
|
|
32
|
+
care, because semver says how much work an upgrade will be. A distribution uses
|
|
33
|
+
each where it belongs: the distribution is versioned by date, the modules inside
|
|
34
|
+
it by semver.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
@fairgarden/core 2024.12.01
|
|
38
|
+
@fairgarden/id 1.2.3
|
|
39
|
+
@fairgarden/design 1.5.6
|
|
40
|
+
@fairgarden/membership 1.0.0
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Modules are git submodules, so the commit is the pin and the version is whatever
|
|
44
|
+
the module's own `package.json` says. Nothing in the distribution repository
|
|
45
|
+
restates it.
|
|
46
|
+
|
|
47
|
+
## Documentation
|
|
48
|
+
|
|
49
|
+
The docs are a site in this repository. Run them with:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm --filter @fairgarden/distribution-docs dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **Versioning** — why two schemes, where a version comes from, and LTS branches
|
|
56
|
+
- **Modules** — apps, packages, and how they resolve
|
|
57
|
+
- **Extending** — one distribution built on another
|
|
58
|
+
- **Deploying** — one deployment, or many
|
|
59
|
+
- **Running it** — dev modes, and a hostname per app
|
|
60
|
+
- **Growing a module** — building in place, then extracting
|
|
61
|
+
- **Releasing a module** — semver, maintenance branches and dist-tags
|
|
62
|
+
- **Submodule urls** — what a build host can actually clone
|
|
63
|
+
- **Commands** and **Functions** — the `fg-dist` CLI and its API
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm add -D @fairgarden/distribution
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or scaffold without installing anything:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpx @fairgarden/distribution init distribution acme --name @acme/core
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
fg-dist init <kind> [dir] # scaffold a distribution, monolith or module
|
|
79
|
+
fg-dist add-module <url> # add a module repository as a submodule
|
|
80
|
+
fg-dist extract <path> # turn a directory here into its own repository
|
|
81
|
+
fg-dist sync # what has moved, and whether the floor holds
|
|
82
|
+
fg-dist bump [name...] # take the newest non-major version
|
|
83
|
+
fg-dist check # fail when this ships older than what it extends
|
|
84
|
+
fg-dist use-https # rewrite ssh submodule urls, and check they are public
|
|
85
|
+
fg-dist readme # write the module versions into the readmes
|
|
86
|
+
fg-dist overrides # resolve modules from the tree, not the registry
|
|
87
|
+
fg-dist workflows # give every module a publishing workflow
|
|
88
|
+
fg-dist canary # stamp a canary version, for CI
|
|
89
|
+
fg-dist release --minor # open the next version on main, and a branch behind it
|
|
90
|
+
fg-dist prerelease --major # start the next line on a branch, leaving main alone
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Mounting a distribution's apps into one Next deployment is a separate concern,
|
|
94
|
+
handled by `@fairgarden/monolith`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** `git@host:owner/name.git` or `https://host/owner/name(.git)` -> `name`. */
|
|
2
|
+
export declare const nameFromUrl: (url: string) => string;
|
|
3
|
+
export interface AddModuleOptions {
|
|
4
|
+
/** Where the submodule goes, relative to the repository. Defaults to `apps/<name>`. */
|
|
5
|
+
at?: string;
|
|
6
|
+
/** Mount name, which becomes the URL segment. Defaults to the repository name. */
|
|
7
|
+
mount?: string;
|
|
8
|
+
/** The monolith whose package.json gains the dependency. Found when omitted. */
|
|
9
|
+
monolith?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Record the URL as given instead of rewriting it to HTTPS. Only do this
|
|
12
|
+
* when nothing but a developer's machine will ever clone the submodule.
|
|
13
|
+
*/
|
|
14
|
+
ssh?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The app that composes the modules, or undefined when there is none.
|
|
18
|
+
*
|
|
19
|
+
* A distribution too complex to serve from one deployment has no monolith at
|
|
20
|
+
* all; each app under `apps/` is deployed on its own.
|
|
21
|
+
*/
|
|
22
|
+
export declare const findMonolith: (root: string, from: string) => Promise<string | undefined>;
|
|
23
|
+
export interface AddModuleResult {
|
|
24
|
+
/** Whether the workspace overrides were updated to include this module. */
|
|
25
|
+
overrides: boolean;
|
|
26
|
+
/** Why they were not, when the workspace could not be edited. */
|
|
27
|
+
overridesError: string | undefined;
|
|
28
|
+
mount: string;
|
|
29
|
+
relativePath: string;
|
|
30
|
+
packageName: string | undefined;
|
|
31
|
+
version: string | undefined;
|
|
32
|
+
/** Whether the monolith's package.json was changed. */
|
|
33
|
+
linked: boolean;
|
|
34
|
+
/** The monolith package.json that was, or should be, updated. */
|
|
35
|
+
monolithPackageJson: string | undefined;
|
|
36
|
+
/** The config the mount was written into. */
|
|
37
|
+
monolithConfig: string | undefined;
|
|
38
|
+
/** Whether the mount was added; false when it was already declared. */
|
|
39
|
+
mounted: boolean;
|
|
40
|
+
/** Why the config could not be edited, when it could not be. */
|
|
41
|
+
mountError: string | undefined;
|
|
42
|
+
/** The URL recorded in .gitmodules, which may differ from the one given. */
|
|
43
|
+
url: string;
|
|
44
|
+
/** Whether that URL was rewritten from SSH. */
|
|
45
|
+
rewritten: boolean;
|
|
46
|
+
/** Whether the module has routes, and so is mounted rather than depended on. */
|
|
47
|
+
isApp: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Add a module repository as a submodule and wire it into the monolith.
|
|
51
|
+
*
|
|
52
|
+
* The submodule is what pins the version, and the dependency is what makes the
|
|
53
|
+
* module resolvable by package name. The mount itself is declared in the
|
|
54
|
+
* monolith's Next config, which is left alone — rewriting someone's config is
|
|
55
|
+
* not something a command should do quietly.
|
|
56
|
+
*/
|
|
57
|
+
export declare const addModule: (cwd: string, url: string, options?: AddModuleOptions) => Promise<AddModuleResult>;
|
|
58
|
+
//# sourceMappingURL=add-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-module.d.ts","sourceRoot":"","sources":["../src/add-module.ts"],"names":[],"mappings":"AAgCA,8EAA8E;AAC9E,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAGzC,CAAA;AAED,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAgBD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,MAAM,MAAM,KACX,OAAO,CAAC,MAAM,GAAG,SAAS,CAiB5B,CAAA;AAED,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,6CAA6C;IAC7C,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAA;IAChB,gEAAgE;IAChE,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAA;IACX,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAA;IAClB,gFAAgF;IAChF,KAAK,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GACpB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,UAAS,gBAAqB,KAC7B,OAAO,CAAC,eAAe,CAwGzB,CAAA"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { readFile, writeFile, readdir, stat, mkdir } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { writeOverrides } from "./overrides.js";
|
|
5
|
+
import { repositoryRoot } from "./submodules.js";
|
|
6
|
+
import { readPackageName } from "./scaffold.js";
|
|
7
|
+
import { addMount, ConfigEditError, declaresMonolith } from "./config-edit.js";
|
|
8
|
+
import { toHttps } from "./git-url.js";
|
|
9
|
+
const CONFIG_FILES = [
|
|
10
|
+
'next.config.ts',
|
|
11
|
+
'next.config.mts',
|
|
12
|
+
'next.config.js',
|
|
13
|
+
'next.config.mjs',
|
|
14
|
+
'next.config.cjs',
|
|
15
|
+
];
|
|
16
|
+
const git = (cwd, args) => {
|
|
17
|
+
try {
|
|
18
|
+
return execFileSync('git', args, {
|
|
19
|
+
cwd,
|
|
20
|
+
encoding: 'utf8',
|
|
21
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
22
|
+
}).trim();
|
|
23
|
+
}
|
|
24
|
+
catch (cause) {
|
|
25
|
+
const stderr = cause instanceof Error && 'stderr' in cause ? String(cause.stderr).trim() : '';
|
|
26
|
+
throw new Error(`git ${args[0]} failed${stderr ? `: ${stderr}` : ''}`, { cause });
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/** `git@host:owner/name.git` or `https://host/owner/name(.git)` -> `name`. */
|
|
30
|
+
export const nameFromUrl = (url) => {
|
|
31
|
+
const last = url.replace(/\.git$/, '').split(/[/:]/).pop() ?? '';
|
|
32
|
+
return last || 'module';
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Whether a directory holds the app that composes modules.
|
|
36
|
+
*
|
|
37
|
+
* Identified by its Next config calling `withMonolith`. A module depends on
|
|
38
|
+
* this package as well, so the dependency alone would match every module.
|
|
39
|
+
*/
|
|
40
|
+
const composesModules = async (dir) => {
|
|
41
|
+
for (const file of CONFIG_FILES) {
|
|
42
|
+
const source = await readFile(path.join(dir, file), 'utf8').catch(() => undefined);
|
|
43
|
+
if (source !== undefined)
|
|
44
|
+
return declaresMonolith(source);
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The app that composes the modules, or undefined when there is none.
|
|
50
|
+
*
|
|
51
|
+
* A distribution too complex to serve from one deployment has no monolith at
|
|
52
|
+
* all; each app under `apps/` is deployed on its own.
|
|
53
|
+
*/
|
|
54
|
+
export const findMonolith = async (root, from) => {
|
|
55
|
+
if (await composesModules(from))
|
|
56
|
+
return from;
|
|
57
|
+
const candidates = [path.join(root, 'apps', 'monolith')];
|
|
58
|
+
for (const parent of ['apps', 'packages']) {
|
|
59
|
+
const entries = await readdir(path.join(root, parent), {
|
|
60
|
+
withFileTypes: true,
|
|
61
|
+
}).catch(() => []);
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
if (entry.isDirectory())
|
|
64
|
+
candidates.push(path.join(root, parent, entry.name));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (await composesModules(candidate))
|
|
69
|
+
return candidate;
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Add a module repository as a submodule and wire it into the monolith.
|
|
75
|
+
*
|
|
76
|
+
* The submodule is what pins the version, and the dependency is what makes the
|
|
77
|
+
* module resolvable by package name. The mount itself is declared in the
|
|
78
|
+
* monolith's Next config, which is left alone — rewriting someone's config is
|
|
79
|
+
* not something a command should do quietly.
|
|
80
|
+
*/
|
|
81
|
+
export const addModule = async (cwd, url, options = {}) => {
|
|
82
|
+
const root = repositoryRoot(cwd);
|
|
83
|
+
if (!root)
|
|
84
|
+
throw new Error(`${cwd} is not inside a git repository.`);
|
|
85
|
+
const name = nameFromUrl(url);
|
|
86
|
+
// Where it lands is decided after cloning, since only the contents say
|
|
87
|
+
// whether this is an app to deploy or a package apps depend on.
|
|
88
|
+
const relativePath = options.at ?? path.join('apps', name);
|
|
89
|
+
const full = path.join(root, relativePath);
|
|
90
|
+
// .gitmodules is committed, so this URL is what every later clone uses.
|
|
91
|
+
const recorded = options.ssh ? url : toHttps(url);
|
|
92
|
+
const rewritten = recorded !== url;
|
|
93
|
+
git(root, ['submodule', 'add', recorded, relativePath]);
|
|
94
|
+
git(root, ['submodule', 'update', '--init', '--recursive', relativePath]);
|
|
95
|
+
let placedAt = relativePath;
|
|
96
|
+
let placedFull = full;
|
|
97
|
+
// A distribution is apps and the packages they share. An app has routes; a
|
|
98
|
+
// package does not, and belongs beside the others rather than under apps/.
|
|
99
|
+
const isApp = await hasRoutes(full);
|
|
100
|
+
if (!options.at && !isApp) {
|
|
101
|
+
const packagePath = path.join('packages', name);
|
|
102
|
+
try {
|
|
103
|
+
await mkdir(path.join(root, 'packages'), { recursive: true });
|
|
104
|
+
git(root, ['mv', relativePath, packagePath]);
|
|
105
|
+
placedAt = packagePath;
|
|
106
|
+
placedFull = path.join(root, packagePath);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Leaving it under apps/ is wrong but harmless; say nothing and carry on.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const packageName = await readPackageName(placedFull);
|
|
113
|
+
const version = await readVersion(placedFull);
|
|
114
|
+
const monolithRoot = options.monolith
|
|
115
|
+
? path.resolve(cwd, options.monolith)
|
|
116
|
+
: await findMonolith(root, cwd);
|
|
117
|
+
const monolithPackageJson = monolithRoot
|
|
118
|
+
? path.join(monolithRoot, 'package.json')
|
|
119
|
+
: undefined;
|
|
120
|
+
// Linked through the workspace, so the version is irrelevant here — only
|
|
121
|
+
// the name is. Guarding on the version left a module with none mounted but
|
|
122
|
+
// never depended on.
|
|
123
|
+
let linked = false;
|
|
124
|
+
if (packageName && monolithPackageJson && monolithRoot !== root) {
|
|
125
|
+
linked = await addDependency(monolithPackageJson, packageName);
|
|
126
|
+
}
|
|
127
|
+
const mount = options.mount ?? name;
|
|
128
|
+
// Only an app is mounted at a path. A package is depended on, not served.
|
|
129
|
+
// A mount is written into the config as a bare string, which is read back as
|
|
130
|
+
// a package name. Without one there is nothing valid to write, so say so
|
|
131
|
+
// rather than record a path that will not resolve.
|
|
132
|
+
const { config, mounted, error } = monolithRoot && isApp
|
|
133
|
+
? packageName
|
|
134
|
+
? await declareMount(monolithRoot, mount, packageName)
|
|
135
|
+
: {
|
|
136
|
+
config: undefined,
|
|
137
|
+
mounted: false,
|
|
138
|
+
error: 'It has no package name, so there is nothing to mount it by. ' +
|
|
139
|
+
'Give it one, or mount it by path yourself.',
|
|
140
|
+
}
|
|
141
|
+
: { config: undefined, mounted: false, error: undefined };
|
|
142
|
+
// The distribution resolves its own modules from the tree, not from their
|
|
143
|
+
// declared ranges — which stop matching as soon as a submodule is bumped
|
|
144
|
+
// past them.
|
|
145
|
+
// The repository, not where the command was run: adding a module from
|
|
146
|
+
// apps/monolith is a supported path, and `cwd` there is not the root.
|
|
147
|
+
//
|
|
148
|
+
// Last, and after everything that matters. The submodule is added and the
|
|
149
|
+
// mount is written by now, so a workspace this cannot edit — a sibling with
|
|
150
|
+
// no checkout, an `overrides:` key someone else wrote — is something to
|
|
151
|
+
// report, not something to fail the whole command over.
|
|
152
|
+
const overrides = await writeOverrides(repositoryRoot(cwd) ?? cwd).catch((error) => ({
|
|
153
|
+
changed: false,
|
|
154
|
+
error: error instanceof Error ? error.message : String(error),
|
|
155
|
+
}));
|
|
156
|
+
return {
|
|
157
|
+
overrides: overrides.changed,
|
|
158
|
+
overridesError: 'error' in overrides ? overrides.error : undefined,
|
|
159
|
+
mount,
|
|
160
|
+
relativePath: placedAt,
|
|
161
|
+
packageName,
|
|
162
|
+
version,
|
|
163
|
+
linked,
|
|
164
|
+
monolithPackageJson,
|
|
165
|
+
monolithConfig: config,
|
|
166
|
+
mounted,
|
|
167
|
+
mountError: error,
|
|
168
|
+
url: recorded,
|
|
169
|
+
rewritten,
|
|
170
|
+
isApp,
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Write the mount into the monolith's Next config.
|
|
175
|
+
*
|
|
176
|
+
* Reports rather than throws when the config is shaped in a way it cannot
|
|
177
|
+
* edit — the submodule is already added by then, and failing the whole command
|
|
178
|
+
* over a line someone can paste would be worse than saying so.
|
|
179
|
+
*/
|
|
180
|
+
const declareMount = async (monolithRoot, mount, module) => {
|
|
181
|
+
for (const file of CONFIG_FILES) {
|
|
182
|
+
const full = path.join(monolithRoot, file);
|
|
183
|
+
let source;
|
|
184
|
+
try {
|
|
185
|
+
source = await readFile(full, 'utf8');
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const result = addMount(source, mount, module);
|
|
192
|
+
if (result.changed)
|
|
193
|
+
await writeFile(full, result.source);
|
|
194
|
+
return { config: full, mounted: result.changed, error: undefined };
|
|
195
|
+
}
|
|
196
|
+
catch (cause) {
|
|
197
|
+
return {
|
|
198
|
+
config: full,
|
|
199
|
+
mounted: false,
|
|
200
|
+
error: cause instanceof ConfigEditError
|
|
201
|
+
? cause.message
|
|
202
|
+
: `The config could not be edited: ${String(cause)}`,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return { config: undefined, mounted: false, error: 'No Next config was found.' };
|
|
207
|
+
};
|
|
208
|
+
const isDirectory = async (candidate) => (await stat(candidate).catch(() => undefined))?.isDirectory() === true;
|
|
209
|
+
/** Whether a checkout is an app, meaning it has routes Next would serve. */
|
|
210
|
+
const hasRoutes = async (root) => {
|
|
211
|
+
for (const dir of ['app', 'pages', path.join('src', 'app'), path.join('src', 'pages')]) {
|
|
212
|
+
if (await isDirectory(path.join(root, dir)))
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
};
|
|
217
|
+
const readVersion = async (root) => {
|
|
218
|
+
try {
|
|
219
|
+
const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
|
|
220
|
+
return typeof pkg.version === 'string' ? pkg.version : undefined;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Depend on the module through the workspace, not on a version.
|
|
228
|
+
*
|
|
229
|
+
* Nothing in a distribution repository pins a module version: the submodule
|
|
230
|
+
* commit is the pin, and the version is whatever the package.json inside that
|
|
231
|
+
* checkout says. `workspace:*` is how that is spelled, and it keeps the
|
|
232
|
+
* repository from carrying a second, stale copy of the same fact.
|
|
233
|
+
*/
|
|
234
|
+
const addDependency = async (packageJsonPath, name) => {
|
|
235
|
+
let pkg;
|
|
236
|
+
try {
|
|
237
|
+
pkg = JSON.parse(await readFile(packageJsonPath, 'utf8'));
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
const dependencies = { ...(pkg.dependencies ?? {}) };
|
|
243
|
+
if (dependencies[name] === 'workspace:*')
|
|
244
|
+
return false;
|
|
245
|
+
dependencies[name] = 'workspace:*';
|
|
246
|
+
pkg.dependencies = Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
247
|
+
await writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
248
|
+
return true;
|
|
249
|
+
};
|
|
250
|
+
//# sourceMappingURL=add-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-module.js","sourceRoot":"","sources":["../src/add-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAE5E,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,YAAY,GAAG;IACnB,gBAAgB;IAChB,iBAAiB;IACjB,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;CAClB,CAAA;AAED,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,IAAc,EAAU,EAAE;IAClD,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC,IAAI,EAAE,CAAA;IACX,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GACV,KAAK,YAAY,KAAK,IAAI,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAChF,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACnF,CAAC;AACH,CAAC,CAAA;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAW,EAAU,EAAE;IACjD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAA;IAChE,OAAO,IAAI,IAAI,QAAQ,CAAA;AACzB,CAAC,CAAA;AAgBD;;;;;GAKG;AACH,MAAM,eAAe,GAAG,KAAK,EAAE,GAAW,EAAoB,EAAE;IAC9D,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAClF,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,IAAY,EACZ,IAAY,EACiB,EAAE;IAC/B,IAAI,MAAM,eAAe,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAE5C,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAA;IACxD,KAAK,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE;YACrD,aAAa,EAAE,IAAI;SACpB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAClB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;QAC/E,CAAC;IACH,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,MAAM,eAAe,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAA;IACxD,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AA6BD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,GAAW,EACX,GAAW,EACX,UAA4B,EAAE,EACJ,EAAE;IAC5B,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;IAChC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,kCAAkC,CAAC,CAAA;IAEpE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC7B,uEAAuE;IACvE,gEAAgE;IAChE,MAAM,YAAY,GAAG,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IAE1C,wEAAwE;IACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACjD,MAAM,SAAS,GAAG,QAAQ,KAAK,GAAG,CAAA;IAElC,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAA;IACvD,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC,CAAA;IAEzE,IAAI,QAAQ,GAAG,YAAY,CAAA;IAC3B,IAAI,UAAU,GAAG,IAAI,CAAA;IAErB,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;QAC/C,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC7D,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,CAAA;YAC5C,QAAQ,GAAG,WAAW,CAAA;YACtB,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,UAAU,CAAC,CAAA;IACrD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,CAAA;IAE7C,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ;QACnC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,MAAM,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACjC,MAAM,mBAAmB,GAAG,YAAY;QACtC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC;QACzC,CAAC,CAAC,SAAS,CAAA;IAEb,yEAAyE;IACzE,2EAA2E;IAC3E,qBAAqB;IACrB,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,IAAI,WAAW,IAAI,mBAAmB,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAChE,MAAM,GAAG,MAAM,aAAa,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAA;IAChE,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAA;IACnC,0EAA0E;IAC1E,6EAA6E;IAC7E,yEAAyE;IACzE,mDAAmD;IACnD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAC9B,YAAY,IAAI,KAAK;QACnB,CAAC,CAAC,WAAW;YACX,CAAC,CAAC,MAAM,YAAY,CAAC,YAAY,EAAE,KAAK,EAAE,WAAW,CAAC;YACtD,CAAC,CAAC;gBACE,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,KAAK;gBACd,KAAK,EACH,8DAA8D;oBAC9D,4CAA4C;aAC/C;QACL,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;IAE7D,0EAA0E;IAC1E,yEAAyE;IACzE,aAAa;IACb,sEAAsE;IACtE,sEAAsE;IACtE,EAAE;IACF,0EAA0E;IAC1E,4EAA4E;IAC5E,wEAAwE;IACxE,wDAAwD;IACxD,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CACtE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC;QACnB,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;KAC9D,CAAC,CACH,CAAA;IAED,OAAO;QACL,SAAS,EAAE,SAAS,CAAC,OAAO;QAC5B,cAAc,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAClE,KAAK;QACL,YAAY,EAAE,QAAQ;QACtB,WAAW;QACX,OAAO;QACP,MAAM;QACN,mBAAmB;QACnB,cAAc,EAAE,MAAM;QACtB,OAAO;QACP,UAAU,EAAE,KAAK;QACjB,GAAG,EAAE,QAAQ;QACb,SAAS;QACT,KAAK;KACN,CAAA;AACH,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,KAAK,EACxB,YAAoB,EACpB,KAAa,EACb,MAAc,EACwE,EAAE;IACxF,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;QAC1C,IAAI,MAAc,CAAA;QAClB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;YAC9C,IAAI,MAAM,CAAC,OAAO;gBAAE,MAAM,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YACxD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,MAAM,EAAE,IAAI;gBACZ,OAAO,EAAE,KAAK;gBACd,KAAK,EACH,KAAK,YAAY,eAAe;oBAC9B,CAAC,CAAC,KAAK,CAAC,OAAO;oBACf,CAAC,CAAC,mCAAmC,MAAM,CAAC,KAAK,CAAC,EAAE;aACzD,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAA;AAClF,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,KAAK,EAAE,SAAiB,EAAoB,EAAE,CAChE,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,IAAI,CAAA;AAExE,4EAA4E;AAC5E,MAAM,SAAS,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE;IACzD,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;QACvF,IAAI,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;IAC1D,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,KAAK,EAAE,IAAY,EAA+B,EAAE;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;QAC/E,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,KAAK,EACzB,eAAuB,EACvB,IAAY,EACM,EAAE;IACpB,IAAI,GAA4B,CAAA;IAChC,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAA;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,YAAY,GAAG,EAAE,GAAG,CAAE,GAAG,CAAC,YAAuC,IAAI,EAAE,CAAC,EAAE,CAAA;IAChF,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,aAAa;QAAE,OAAO,KAAK,CAAA;IAEtD,YAAY,CAAC,IAAI,CAAC,GAAG,aAAa,CAAA;IAClC,GAAG,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CACnC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CACpE,CAAA;IAED,MAAM,SAAS,CAAC,eAAe,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;IACrE,OAAO,IAAI,CAAA;AACb,CAAC,CAAA"}
|
package/dist/canary.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** What is on the `canary` tag now. */
|
|
2
|
+
export interface Published {
|
|
3
|
+
version?: string;
|
|
4
|
+
gitSha?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The next canary version.
|
|
8
|
+
*
|
|
9
|
+
* The suffix counts this canary's position within its own release cycle, read
|
|
10
|
+
* from the registry. A CI run counter would look similar and be wrong: it skips
|
|
11
|
+
* numbers whenever a run fails or deduplicates, which makes published versions
|
|
12
|
+
* a record of workflow bookkeeping rather than of releases.
|
|
13
|
+
*/
|
|
14
|
+
export declare const nextCanary: (version: string, published: string | undefined) => string;
|
|
15
|
+
export interface CanaryResult {
|
|
16
|
+
name: string;
|
|
17
|
+
/**
|
|
18
|
+
* Whether npm will generate provenance for this package. It refuses to for
|
|
19
|
+
* anything not published publicly, and refusing is a failed publish.
|
|
20
|
+
*/
|
|
21
|
+
provenance: boolean;
|
|
22
|
+
/** The version written into the manifest, or the one already published. */
|
|
23
|
+
version: string;
|
|
24
|
+
/** True when this commit is already on the canary tag, so there is nothing to do. */
|
|
25
|
+
skip: boolean;
|
|
26
|
+
sha: string;
|
|
27
|
+
}
|
|
28
|
+
export interface CanaryOptions {
|
|
29
|
+
/** The commit being built. Defaults to `GITHUB_SHA`. */
|
|
30
|
+
sha?: string;
|
|
31
|
+
/** What is on the canary tag. Defaults to asking npm. */
|
|
32
|
+
published?: Published;
|
|
33
|
+
/** Work out the version and report it, without touching the manifest. */
|
|
34
|
+
dryRun?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Stamp a canary version into the manifest, ready to publish.
|
|
38
|
+
*
|
|
39
|
+
* Returns without writing when the canary tag already points at this commit,
|
|
40
|
+
* which is what makes a nightly build with nothing new a no-op rather than a
|
|
41
|
+
* version bump.
|
|
42
|
+
*/
|
|
43
|
+
export declare const stampCanary: (root: string, { sha, published, dryRun }?: CanaryOptions) => Promise<CanaryResult>;
|
|
44
|
+
/** Hand a result to the workflow step that decides whether to publish. */
|
|
45
|
+
export declare const reportToActions: (result: CanaryResult) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a manifest publishes publicly.
|
|
48
|
+
*
|
|
49
|
+
* npm generates provenance only for public packages, and asking for it on a
|
|
50
|
+
* restricted one fails the publish. A scoped package is restricted unless it
|
|
51
|
+
* says otherwise, which is why the default here is the pessimistic one.
|
|
52
|
+
*/
|
|
53
|
+
export declare const isPublic: (manifest: {
|
|
54
|
+
name?: unknown;
|
|
55
|
+
private?: unknown;
|
|
56
|
+
publishConfig?: unknown;
|
|
57
|
+
}) => boolean;
|
|
58
|
+
//# sourceMappingURL=canary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canary.d.ts","sourceRoot":"","sources":["../src/canary.ts"],"names":[],"mappings":"AAoBA,uCAAuC;AACvC,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,WAAW,MAAM,GAAG,SAAS,KAAG,MA8B3E,CAAA;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACtB,MAAM,MAAM,EACZ,6BAAmE,aAAkB,KACpF,OAAO,CAAC,YAAY,CAqCtB,CAAA;AAED,0EAA0E;AAC1E,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,KAAG,OAAO,CAAC,IAAI,CAUxE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,UAAU;IACjC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,KAAG,OASH,CAAA"}
|
package/dist/canary.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { appendFile, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
/** Ask npm about a published version, returning undefined when it says nothing. */
|
|
6
|
+
const view = (spec, field) => {
|
|
7
|
+
try {
|
|
8
|
+
const value = execFileSync('npm', ['view', spec, field], {
|
|
9
|
+
encoding: 'utf8',
|
|
10
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
11
|
+
}).trim();
|
|
12
|
+
return value === '' ? undefined : value;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Not published yet, or no registry to ask. Either way there is no
|
|
16
|
+
// previous canary, which is the same thing as far as the next one goes.
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The next canary version.
|
|
22
|
+
*
|
|
23
|
+
* The suffix counts this canary's position within its own release cycle, read
|
|
24
|
+
* from the registry. A CI run counter would look similar and be wrong: it skips
|
|
25
|
+
* numbers whenever a run fails or deduplicates, which makes published versions
|
|
26
|
+
* a record of workflow bookkeeping rather than of releases.
|
|
27
|
+
*/
|
|
28
|
+
export const nextCanary = (version, published) => {
|
|
29
|
+
const parsed = semver.parse(version);
|
|
30
|
+
if (!parsed)
|
|
31
|
+
throw new Error(`\`${version}\` is not a version to build a canary from.`);
|
|
32
|
+
// A canary has to sort below every real version of what it is building
|
|
33
|
+
// towards, or somebody's range resolves to one.
|
|
34
|
+
//
|
|
35
|
+
// The familiar `1.7.0-canary.3` shape only half does that. It is below 1.7.0,
|
|
36
|
+
// but `canary` is above `alpha` and `beta` as a string, so while main sits at
|
|
37
|
+
// 2.0.0 with 2.0.0-beta.2 published, a consumer on `^2.0.0-beta.2` gets the
|
|
38
|
+
// canary. Naming it after the prerelease it follows fails the same way from
|
|
39
|
+
// the other side.
|
|
40
|
+
//
|
|
41
|
+
// Semver ranks numeric identifiers below alphanumeric ones, so a numeric
|
|
42
|
+
// first identifier is the one slot beneath all of them: `2.0.0-0.canary.3` is
|
|
43
|
+
// below alpha, beta, rc and anything else anyone names, which puts it under
|
|
44
|
+
// the floor of every prerelease range. One shape for every target, so there
|
|
45
|
+
// is no case where this is nearly right.
|
|
46
|
+
const prefix = `${parsed.major}.${parsed.minor}.${parsed.patch}-0.canary.`;
|
|
47
|
+
// Counted from what is on the tag rather than from a CI run number, which
|
|
48
|
+
// skips whenever a run fails or deduplicates. The prefix ignores the
|
|
49
|
+
// prerelease, so the count carries on across alpha, beta and the release
|
|
50
|
+
// itself instead of restarting at each one.
|
|
51
|
+
const count = published && published.startsWith(prefix)
|
|
52
|
+
? Number(published.slice(prefix.length)) + 1
|
|
53
|
+
: 0;
|
|
54
|
+
return `${prefix}${Number.isFinite(count) ? count : 0}`;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Stamp a canary version into the manifest, ready to publish.
|
|
58
|
+
*
|
|
59
|
+
* Returns without writing when the canary tag already points at this commit,
|
|
60
|
+
* which is what makes a nightly build with nothing new a no-op rather than a
|
|
61
|
+
* version bump.
|
|
62
|
+
*/
|
|
63
|
+
export const stampCanary = async (root, { sha = process.env.GITHUB_SHA ?? '', published, dryRun = false } = {}) => {
|
|
64
|
+
const file = path.join(root, 'package.json');
|
|
65
|
+
const manifest = JSON.parse(await readFile(file, 'utf8'));
|
|
66
|
+
if (typeof manifest.name !== 'string' || typeof manifest.version !== 'string') {
|
|
67
|
+
throw new Error('package.json needs a name and a version to stamp a canary.');
|
|
68
|
+
}
|
|
69
|
+
const current = published ??
|
|
70
|
+
{
|
|
71
|
+
version: view(`${manifest.name}@canary`, 'version'),
|
|
72
|
+
gitSha: view(`${manifest.name}@canary`, 'gitSha'),
|
|
73
|
+
};
|
|
74
|
+
const provenance = isPublic(manifest);
|
|
75
|
+
if (sha && current.gitSha === sha) {
|
|
76
|
+
return {
|
|
77
|
+
name: manifest.name,
|
|
78
|
+
version: current.version ?? '',
|
|
79
|
+
skip: true,
|
|
80
|
+
provenance,
|
|
81
|
+
sha,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const version = nextCanary(manifest.version, current.version);
|
|
85
|
+
if (!dryRun) {
|
|
86
|
+
await writeFile(file, `${JSON.stringify({ ...manifest, version, gitSha: sha }, null, 2)}\n`);
|
|
87
|
+
}
|
|
88
|
+
return { name: manifest.name, version, skip: false, provenance, sha };
|
|
89
|
+
};
|
|
90
|
+
/** Hand a result to the workflow step that decides whether to publish. */
|
|
91
|
+
export const reportToActions = async (result) => {
|
|
92
|
+
const output = process.env.GITHUB_OUTPUT;
|
|
93
|
+
if (!output)
|
|
94
|
+
return;
|
|
95
|
+
await appendFile(output, `version=${result.version}\n` +
|
|
96
|
+
`skip=${result.skip ? 'true' : 'false'}\n` +
|
|
97
|
+
`provenance=${result.provenance ? 'true' : 'false'}\n`);
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Whether a manifest publishes publicly.
|
|
101
|
+
*
|
|
102
|
+
* npm generates provenance only for public packages, and asking for it on a
|
|
103
|
+
* restricted one fails the publish. A scoped package is restricted unless it
|
|
104
|
+
* says otherwise, which is why the default here is the pessimistic one.
|
|
105
|
+
*/
|
|
106
|
+
export const isPublic = (manifest) => {
|
|
107
|
+
if (manifest.private === true)
|
|
108
|
+
return false;
|
|
109
|
+
const access = manifest.publishConfig?.access;
|
|
110
|
+
if (access === 'public')
|
|
111
|
+
return true;
|
|
112
|
+
if (access === 'restricted')
|
|
113
|
+
return false;
|
|
114
|
+
// Unscoped packages are public; scoped ones are not, unless told to be.
|
|
115
|
+
return typeof manifest.name === 'string' && !manifest.name.startsWith('@');
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=canary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canary.js","sourceRoot":"","sources":["../src/canary.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|