@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
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
/** Run git, returning undefined rather than throwing when it fails. */
|
|
6
|
+
const git = (cwd, args) => {
|
|
7
|
+
try {
|
|
8
|
+
return execFileSync('git', args, {
|
|
9
|
+
cwd,
|
|
10
|
+
encoding: 'utf8',
|
|
11
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
12
|
+
}).trim();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const lines = (value) => value ? value.split('\n').filter(Boolean) : [];
|
|
19
|
+
/**
|
|
20
|
+
* Whether this repository ships modules, as opposed to being one.
|
|
21
|
+
*
|
|
22
|
+
* Read from `.gitmodules`, which is committed, rather than from
|
|
23
|
+
* {@link submodules}, which only reports checkouts that are actually there. A
|
|
24
|
+
* clone made without `--recurse-submodules` has the file and none of the
|
|
25
|
+
* checkouts, and treating that as a module would write a module's readme over
|
|
26
|
+
* the distribution's and let it be released as if it were one.
|
|
27
|
+
*/
|
|
28
|
+
export const isDistribution = (root) => existsSync(path.join(root, '.gitmodules')) &&
|
|
29
|
+
(git(root, ['config', '--file', '.gitmodules', '--get-regexp', 'path']) ?? '') !== '';
|
|
30
|
+
/**
|
|
31
|
+
* Submodules that are missing their checkout.
|
|
32
|
+
*
|
|
33
|
+
* Anything that reads a module's own package.json needs to say so rather than
|
|
34
|
+
* quietly leave it out.
|
|
35
|
+
*/
|
|
36
|
+
export const uninitialised = (root) => {
|
|
37
|
+
const config = git(root, ['config', '--file', '.gitmodules', '--get-regexp', 'path']);
|
|
38
|
+
return lines(config)
|
|
39
|
+
.map((line) => line.split(' ')[1])
|
|
40
|
+
.filter((relativePath) => Boolean(relativePath))
|
|
41
|
+
.filter((relativePath) => !existsSync(path.join(root, relativePath, '.git')));
|
|
42
|
+
};
|
|
43
|
+
/** The repository holding the submodules, which is not the monolith directory. */
|
|
44
|
+
export const repositoryRoot = (from) => git(from, ['rev-parse', '--show-toplevel']);
|
|
45
|
+
/**
|
|
46
|
+
* Submodules as `.gitmodules` records them, with the commit each is pinned to.
|
|
47
|
+
*
|
|
48
|
+
* Uninitialised submodules are left out — there is nothing to inspect until
|
|
49
|
+
* they are checked out.
|
|
50
|
+
*/
|
|
51
|
+
export const submodules = (root) => {
|
|
52
|
+
const config = git(root, ['config', '--file', '.gitmodules', '--get-regexp', 'path']);
|
|
53
|
+
const found = [];
|
|
54
|
+
for (const line of lines(config)) {
|
|
55
|
+
const [key, relativePath] = line.split(' ');
|
|
56
|
+
if (!relativePath)
|
|
57
|
+
continue;
|
|
58
|
+
const name = key.slice('submodule.'.length, -'.path'.length);
|
|
59
|
+
const url = git(root, ['config', '--file', '.gitmodules', '--get', `submodule.${name}.url`]) ?? '';
|
|
60
|
+
const full = path.join(root, relativePath);
|
|
61
|
+
// An uninitialised submodule is an empty directory, and git run inside one
|
|
62
|
+
// finds the distribution instead — reporting the distribution's own tags
|
|
63
|
+
// as the module's, and checking one out onto the distribution's HEAD.
|
|
64
|
+
if (!existsSync(path.join(full, '.git')))
|
|
65
|
+
continue;
|
|
66
|
+
const pinned = git(full, ['rev-parse', 'HEAD']);
|
|
67
|
+
if (!pinned)
|
|
68
|
+
continue;
|
|
69
|
+
found.push({
|
|
70
|
+
name: path.basename(relativePath),
|
|
71
|
+
configName: name,
|
|
72
|
+
relativePath,
|
|
73
|
+
path: full,
|
|
74
|
+
url,
|
|
75
|
+
pinned,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return found;
|
|
79
|
+
};
|
|
80
|
+
/** Tags that name a version, newest first. */
|
|
81
|
+
const versionTags = (cwd) => lines(git(cwd, ['tag', '--list']))
|
|
82
|
+
.filter((tag) => semver.valid(tag) !== null)
|
|
83
|
+
.sort((a, b) => semver.rcompare(a, b));
|
|
84
|
+
/** The branch tip the submodule is tracking, for counting unreleased work. */
|
|
85
|
+
const upstream = (cwd) => {
|
|
86
|
+
for (const ref of ['origin/HEAD', 'origin/main', 'origin/master', 'main', 'master']) {
|
|
87
|
+
if (git(cwd, ['rev-parse', '--verify', '--quiet', ref]))
|
|
88
|
+
return ref;
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Compare a submodule's pinned commit against the versions its repository has.
|
|
94
|
+
*
|
|
95
|
+
* Only tags that name a version are considered an upgrade. Commits past the
|
|
96
|
+
* newest tag are counted and reported, but never bumped to — a commit carries
|
|
97
|
+
* no statement about what changed, which is the whole point of the version.
|
|
98
|
+
*/
|
|
99
|
+
export const inspect = (submodule, { fetch = true } = {}) => {
|
|
100
|
+
const cwd = submodule.path;
|
|
101
|
+
let fetched;
|
|
102
|
+
if (fetch) {
|
|
103
|
+
fetched = git(cwd, ['fetch', '--tags', '--quiet']) !== undefined;
|
|
104
|
+
}
|
|
105
|
+
const dirty = (git(cwd, ['status', '--porcelain']) ?? '') !== '';
|
|
106
|
+
const tags = versionTags(cwd);
|
|
107
|
+
// Read HEAD rather than trusting the commit captured at discovery: a bump
|
|
108
|
+
// moves the checkout, and anything inspecting afterwards must see that.
|
|
109
|
+
const head = git(cwd, ['rev-parse', 'HEAD']) ?? submodule.pinned;
|
|
110
|
+
const exactTag = lines(git(cwd, ['tag', '--points-at', head])).find((tag) => semver.valid(tag) !== null);
|
|
111
|
+
// Otherwise the newest tag the pinned commit descends from.
|
|
112
|
+
const describedTag = git(cwd, ['describe', '--tags', '--abbrev=0', '--match', '*', head]);
|
|
113
|
+
const described = describedTag && semver.valid(describedTag) !== null ? describedTag : undefined;
|
|
114
|
+
const current = exactTag ?? described;
|
|
115
|
+
const available = current
|
|
116
|
+
? tags.filter((tag) => semver.gt(tag, current))
|
|
117
|
+
: tags;
|
|
118
|
+
const upgrades = {};
|
|
119
|
+
for (const tag of available) {
|
|
120
|
+
// semver.diff also returns premajor, preminor and prepatch. Bucketing
|
|
121
|
+
// those as patch would let a jump to 1.0.0-alpha.0 apply without --major,
|
|
122
|
+
// which matters most for the 0.x.y-alpha.n versions this is built for.
|
|
123
|
+
const difference = current ? semver.diff(current, tag) : 'major';
|
|
124
|
+
const bucket = difference === 'major' || difference === 'premajor'
|
|
125
|
+
? 'major'
|
|
126
|
+
: difference === 'minor' || difference === 'preminor'
|
|
127
|
+
? 'minor'
|
|
128
|
+
: 'patch';
|
|
129
|
+
// available is newest first, so the first of each kind is the newest.
|
|
130
|
+
upgrades[bucket] ??= tag;
|
|
131
|
+
}
|
|
132
|
+
const tip = upstream(cwd);
|
|
133
|
+
const from = available[0] ?? current ?? head;
|
|
134
|
+
const untagged = tip
|
|
135
|
+
? Number(git(cwd, ['rev-list', '--count', `${from}..${tip}`]) ?? '0')
|
|
136
|
+
: 0;
|
|
137
|
+
return {
|
|
138
|
+
submodule,
|
|
139
|
+
head,
|
|
140
|
+
current,
|
|
141
|
+
exact: exactTag !== undefined,
|
|
142
|
+
available,
|
|
143
|
+
upgrades,
|
|
144
|
+
untagged,
|
|
145
|
+
dirty,
|
|
146
|
+
fetched,
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* The version a bump should move to, or undefined when there is nothing to do.
|
|
151
|
+
*
|
|
152
|
+
* Major upgrades are held back unless asked for: they are the ones that need a
|
|
153
|
+
* person to read a changelog first.
|
|
154
|
+
*/
|
|
155
|
+
export const target = (state, { major = false } = {}) => {
|
|
156
|
+
const candidates = [
|
|
157
|
+
state.upgrades.patch,
|
|
158
|
+
state.upgrades.minor,
|
|
159
|
+
...(major ? [state.upgrades.major] : []),
|
|
160
|
+
].filter((tag) => tag !== undefined);
|
|
161
|
+
if (candidates.length === 0)
|
|
162
|
+
return undefined;
|
|
163
|
+
return candidates.sort((a, b) => semver.rcompare(a, b))[0];
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Record a different URL for a submodule.
|
|
167
|
+
*
|
|
168
|
+
* `.gitmodules` is what gets committed, and `submodule sync` copies it into the
|
|
169
|
+
* local config so the existing checkout uses it too. The caller commits.
|
|
170
|
+
*/
|
|
171
|
+
export const setUrl = (root, submodule, url) => {
|
|
172
|
+
const written = git(root, [
|
|
173
|
+
'config',
|
|
174
|
+
'--file',
|
|
175
|
+
'.gitmodules',
|
|
176
|
+
`submodule.${submodule.configName}.url`,
|
|
177
|
+
url,
|
|
178
|
+
]);
|
|
179
|
+
if (written === undefined) {
|
|
180
|
+
throw new Error(`Could not set the url for ${submodule.relativePath}.`);
|
|
181
|
+
}
|
|
182
|
+
git(root, ['submodule', 'sync', '--quiet', '--', submodule.relativePath]);
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Whether a remote can be read with no credentials at all.
|
|
186
|
+
*
|
|
187
|
+
* This is the question a build host asks. Prompting is disabled so a private
|
|
188
|
+
* repository fails immediately instead of waiting for a password nobody is
|
|
189
|
+
* there to type.
|
|
190
|
+
*/
|
|
191
|
+
export const isPublic = (url, timeout = 15_000) => {
|
|
192
|
+
try {
|
|
193
|
+
execFileSync('git', ['ls-remote', '--exit-code', url, 'HEAD'], {
|
|
194
|
+
encoding: 'utf8',
|
|
195
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
196
|
+
timeout,
|
|
197
|
+
env: {
|
|
198
|
+
...process.env,
|
|
199
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
200
|
+
GIT_ASKPASS: 'echo',
|
|
201
|
+
GIT_SSH_COMMAND: 'ssh -oBatchMode=yes',
|
|
202
|
+
GIT_CONFIG_COUNT: '1',
|
|
203
|
+
GIT_CONFIG_KEY_0: 'credential.helper',
|
|
204
|
+
GIT_CONFIG_VALUE_0: '',
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
/** Move a submodule's checkout to a tag. The caller commits the new pointer. */
|
|
214
|
+
export const moveTo = (submodule, tag) => {
|
|
215
|
+
// Belt and braces: never check out inside something that is not the
|
|
216
|
+
// submodule's own repository.
|
|
217
|
+
if (!existsSync(path.join(submodule.path, '.git'))) {
|
|
218
|
+
throw new Error(`${submodule.relativePath} is not checked out, so there is nothing to move.`);
|
|
219
|
+
}
|
|
220
|
+
const result = git(submodule.path, ['checkout', '--quiet', `refs/tags/${tag}`]);
|
|
221
|
+
if (result === undefined) {
|
|
222
|
+
throw new Error(`Could not check out ${tag} in ${submodule.relativePath}.`);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=submodules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submodules.js","sourceRoot":"","sources":["../src/submodules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,uEAAuE;AACvE,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,IAAc,EAAsB,EAAE;IAC9D,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,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAA;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED,MAAM,KAAK,GAAG,CAAC,KAAyB,EAAY,EAAE,CACpD,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAEhD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAW,EAAE,CACtD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC1C,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAA;AAEvF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAY,EAAE;IACtD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAA;IAErF,OAAO,KAAK,CAAC,MAAM,CAAC;SACjB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SACjC,MAAM,CAAC,CAAC,YAAY,EAA0B,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;SACvE,MAAM,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AACjF,CAAC,CAAA;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAsB,EAAE,CACjE,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAA;AAe7C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAe,EAAE;IACtD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAA;IACrF,MAAM,KAAK,GAAgB,EAAE,CAAA;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3C,IAAI,CAAC,YAAY;YAAE,SAAQ;QAE3B,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAC5D,MAAM,GAAG,GACP,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QACxF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;QAE1C,2EAA2E;QAC3E,yEAAyE;QACzE,sEAAsE;QACtE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAAE,SAAQ;QAElD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;QAC/C,IAAI,CAAC,MAAM;YAAE,SAAQ;QAErB,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;YACjC,UAAU,EAAE,IAAI;YAChB,YAAY;YACZ,IAAI,EAAE,IAAI;YACV,GAAG;YACH,MAAM;SACP,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,8CAA8C;AAC9C,MAAM,WAAW,GAAG,CAAC,GAAW,EAAY,EAAE,CAC5C,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;KAC/B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;KAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;AAE1C,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAsB,EAAE;IACnD,KAAK,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;QACpF,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;YAAE,OAAO,GAAG,CAAA;IACrE,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAwBD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,SAAoB,EACpB,EAAE,KAAK,GAAG,IAAI,KAA0B,EAAE,EAC1B,EAAE;IAClB,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAA;IAE1B,IAAI,OAA4B,CAAA;IAChC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,KAAK,SAAS,CAAA;IAClE,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAA;IAChE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAE7B,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,IAAI,SAAS,CAAC,MAAM,CAAA;IAEhE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CACjE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,CACpC,CAAA;IACD,4DAA4D;IAC5D,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;IACzF,MAAM,SAAS,GACb,YAAY,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAA;IAEhF,MAAM,OAAO,GAAG,QAAQ,IAAI,SAAS,CAAA;IACrC,MAAM,SAAS,GAAG,OAAO;QACvB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC,CAAC,IAAI,CAAA;IAER,MAAM,QAAQ,GAAyC,EAAE,CAAA;IACzD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,sEAAsE;QACtE,0EAA0E;QAC1E,uEAAuE;QACvE,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;QAChE,MAAM,MAAM,GACV,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,UAAU;YACjD,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,UAAU;gBACnD,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,OAAO,CAAA;QACf,sEAAsE;QACtE,QAAQ,CAAC,MAAM,CAAC,KAAK,GAAG,CAAA;IAC1B,CAAC;IAED,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IACzB,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,IAAI,IAAI,CAAA;IAC5C,MAAM,QAAQ,GAAG,GAAG;QAClB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC;QACrE,CAAC,CAAC,CAAC,CAAA;IAEL,OAAO;QACL,SAAS;QACT,IAAI;QACJ,OAAO;QACP,KAAK,EAAE,QAAQ,KAAK,SAAS;QAC7B,SAAS;QACT,QAAQ;QACR,QAAQ;QACR,KAAK;QACL,OAAO;KACR,CAAA;AACH,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,KAAqB,EACrB,EAAE,KAAK,GAAG,KAAK,KAA0B,EAAE,EACvB,EAAE;IACtB,MAAM,UAAU,GAAG;QACjB,KAAK,CAAC,QAAQ,CAAC,KAAK;QACpB,KAAK,CAAC,QAAQ,CAAC,KAAK;QACpB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACzC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,CAAA;IAEnD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAC7C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5D,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,SAAoB,EAAE,GAAW,EAAQ,EAAE;IAC9E,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE;QACxB,QAAQ;QACR,QAAQ;QACR,aAAa;QACb,aAAa,SAAS,CAAC,UAAU,MAAM;QACvC,GAAG;KACJ,CAAC,CAAA;IACF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,6BAA6B,SAAS,CAAC,YAAY,GAAG,CAAC,CAAA;IACzE,CAAC;IACD,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,CAAA;AAC3E,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,OAAO,GAAG,MAAM,EAAW,EAAE;IACjE,IAAI,CAAC;QACH,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE;YAC7D,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO;YACP,GAAG,EAAE;gBACH,GAAG,OAAO,CAAC,GAAG;gBACd,mBAAmB,EAAE,GAAG;gBACxB,WAAW,EAAE,MAAM;gBACnB,eAAe,EAAE,qBAAqB;gBACtC,gBAAgB,EAAE,GAAG;gBACrB,gBAAgB,EAAE,mBAAmB;gBACrC,kBAAkB,EAAE,EAAE;aACvB;SACF,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC,CAAA;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,SAAoB,EAAE,GAAW,EAAQ,EAAE;IAChE,oEAAoE;IACpE,8BAA8B;IAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,GAAG,SAAS,CAAC,YAAY,mDAAmD,CAC7E,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,aAAa,GAAG,EAAE,CAAC,CAAC,CAAA;IAC/E,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,OAAO,SAAS,CAAC,YAAY,GAAG,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Files } from './scaffold.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The publishing workflow a module carries.
|
|
4
|
+
*
|
|
5
|
+
* A module releases on its own schedule, so the workflow lives in the module
|
|
6
|
+
* rather than the distribution. Authentication is npm trusted publishing over
|
|
7
|
+
* OIDC — there is no token to leak, and the package must already exist on npm
|
|
8
|
+
* with a trusted publisher pointing at this repository and this file.
|
|
9
|
+
*/
|
|
10
|
+
export declare const publishWorkflow: (packageName: string) => Files;
|
|
11
|
+
export interface WorkflowUpdate {
|
|
12
|
+
relativePath: string;
|
|
13
|
+
files: string[];
|
|
14
|
+
skipped: boolean;
|
|
15
|
+
/** Whether `private: true` was taken off, so the module can publish at all. */
|
|
16
|
+
unprivated: boolean;
|
|
17
|
+
/** True when nothing in the manifest says what the tarball should contain. */
|
|
18
|
+
missingFiles: boolean;
|
|
19
|
+
/** True when nothing says whether the package publishes publicly. */
|
|
20
|
+
missingAccess: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The scripts the workflow calls, and a module releases by hand with.
|
|
24
|
+
*
|
|
25
|
+
* They are named rather than spelled out in the workflow so that every
|
|
26
|
+
* module's workflow is the same file, and so `pnpm release` is the answer to
|
|
27
|
+
* "how do I release this" wherever you are standing.
|
|
28
|
+
*/
|
|
29
|
+
export declare const releaseScripts: (packageName: string) => Record<string, string>;
|
|
30
|
+
export interface ManifestChange {
|
|
31
|
+
changed: boolean;
|
|
32
|
+
unprivated: boolean;
|
|
33
|
+
missingFiles: boolean;
|
|
34
|
+
missingAccess: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Give a module what it needs to publish itself.
|
|
38
|
+
*
|
|
39
|
+
* One pass over the manifest rather than several: a structural rewrite undoes
|
|
40
|
+
* a careful textual splice, so doing both would mean the second silently
|
|
41
|
+
* discarding the first's formatting. Two-space JSON is what npm and pnpm write
|
|
42
|
+
* anyway.
|
|
43
|
+
*/
|
|
44
|
+
export declare const updateManifest: (moduleRoot: string, packageName: string, version: string | undefined, packageManager: string | undefined, { write }?: {
|
|
45
|
+
write?: boolean;
|
|
46
|
+
}) => Promise<ManifestChange>;
|
|
47
|
+
/**
|
|
48
|
+
* Give every module a publishing workflow.
|
|
49
|
+
*
|
|
50
|
+
* A module that already has one is left alone: it may have been changed on
|
|
51
|
+
* purpose, and overwriting it would be the kind of help nobody asked for.
|
|
52
|
+
*/
|
|
53
|
+
export declare const writeWorkflows: (root: string, { force, dryRun }?: {
|
|
54
|
+
force?: boolean;
|
|
55
|
+
dryRun?: boolean;
|
|
56
|
+
}) => Promise<WorkflowUpdate[]>;
|
|
57
|
+
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAE1C;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,KAAG,KAmLpD,CAAA;AAEF,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,+EAA+E;IAC/E,UAAU,EAAE,OAAO,CAAA;IACnB,8EAA8E;IAC9E,YAAY,EAAE,OAAO,CAAA;IACrB,qEAAqE;IACrE,aAAa,EAAE,OAAO,CAAA;CACvB;AA2BD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,aAAa,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAQzE,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,YAAY,EAAE,OAAO,CAAA;IACrB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,MAAM,EAClB,aAAa,MAAM,EACnB,SAAS,MAAM,GAAG,SAAS,EAC3B,gBAAgB,MAAM,GAAG,SAAS,EAClC,YAAkB;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,KACzC,OAAO,CAAC,cAAc,CA8DxB,CAAA;AAGD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,oBAAmC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,KAC5E,OAAO,CAAC,cAAc,EAAE,CAiE1B,CAAA"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { submodules } from "./submodules.js";
|
|
7
|
+
/**
|
|
8
|
+
* The publishing workflow a module carries.
|
|
9
|
+
*
|
|
10
|
+
* A module releases on its own schedule, so the workflow lives in the module
|
|
11
|
+
* rather than the distribution. Authentication is npm trusted publishing over
|
|
12
|
+
* OIDC — there is no token to leak, and the package must already exist on npm
|
|
13
|
+
* with a trusted publisher pointing at this repository and this file.
|
|
14
|
+
*/
|
|
15
|
+
export const publishWorkflow = (packageName) => ({
|
|
16
|
+
'.github/workflows/publish.yml': `name: Publish
|
|
17
|
+
|
|
18
|
+
# npm trusted publishing is configured against one workflow file per package,
|
|
19
|
+
# so canary and release both live here. There is no npm token: the package must
|
|
20
|
+
# already exist on npm with a trusted publisher pointing at this repository and
|
|
21
|
+
# this file. Bootstrap a new package by publishing it once from a workstation,
|
|
22
|
+
# then configure trusted publishing on npmjs.com.
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
push:
|
|
26
|
+
branches:
|
|
27
|
+
- main
|
|
28
|
+
schedule:
|
|
29
|
+
- cron: '0 0 * * *' # Daily at midnight UTC
|
|
30
|
+
workflow_dispatch:
|
|
31
|
+
inputs:
|
|
32
|
+
dist-tag:
|
|
33
|
+
description: 'npm dist tag to publish the release to'
|
|
34
|
+
required: false
|
|
35
|
+
type: string
|
|
36
|
+
default: 'latest'
|
|
37
|
+
dry-run:
|
|
38
|
+
description: 'Pack and validate without publishing'
|
|
39
|
+
required: false
|
|
40
|
+
type: boolean
|
|
41
|
+
default: false
|
|
42
|
+
|
|
43
|
+
permissions: {}
|
|
44
|
+
|
|
45
|
+
concurrency:
|
|
46
|
+
group: publish-\${{ github.ref }}
|
|
47
|
+
cancel-in-progress: false
|
|
48
|
+
|
|
49
|
+
jobs:
|
|
50
|
+
canary:
|
|
51
|
+
name: Publish canary
|
|
52
|
+
# Lockfile bumps do not warrant a canary.
|
|
53
|
+
if: >
|
|
54
|
+
github.event_name == 'schedule'
|
|
55
|
+
|| (github.event_name == 'push' && github.event.head_commit.author.name != 'renovate[bot]')
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
permissions:
|
|
58
|
+
contents: read
|
|
59
|
+
id-token: write # Required for provenance and trusted publishing
|
|
60
|
+
steps:
|
|
61
|
+
- name: Checkout
|
|
62
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
63
|
+
with:
|
|
64
|
+
persist-credentials: false
|
|
65
|
+
|
|
66
|
+
- name: Prepare for publishing
|
|
67
|
+
uses: ./.github/actions/publish-prepare
|
|
68
|
+
|
|
69
|
+
- name: Stamp the canary version
|
|
70
|
+
id: canary
|
|
71
|
+
# \`fg-dist canary\` works out the version and whether there is anything
|
|
72
|
+
# to publish: a canary records the commit it was built from, so a
|
|
73
|
+
# nightly run with nothing new is a no-op rather than a version bump.
|
|
74
|
+
run: pnpm run canary
|
|
75
|
+
|
|
76
|
+
- name: Publish to npm
|
|
77
|
+
if: steps.canary.outputs.skip != 'true'
|
|
78
|
+
# Provenance only where npm will generate it: it refuses for anything
|
|
79
|
+
# not published publicly, and refusing is a failed publish.
|
|
80
|
+
run: |
|
|
81
|
+
if [ "\${{ steps.canary.outputs.provenance }}" = "true" ]; then
|
|
82
|
+
npm publish --tag canary --provenance
|
|
83
|
+
else
|
|
84
|
+
npm publish --tag canary
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
- name: Summary
|
|
88
|
+
run: |
|
|
89
|
+
if [ "\${{ steps.canary.outputs.skip }}" = "true" ]; then
|
|
90
|
+
echo "\\\`\${{ steps.canary.outputs.version }}\\\` is already the canary for this commit." >> "\$GITHUB_STEP_SUMMARY"
|
|
91
|
+
else
|
|
92
|
+
echo "Published \\\`${packageName}@\${{ steps.canary.outputs.version }}\\\` to the \\\`canary\\\` tag." >> "\$GITHUB_STEP_SUMMARY"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
release:
|
|
96
|
+
name: Publish release
|
|
97
|
+
if: github.event_name == 'workflow_dispatch'
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
permissions:
|
|
100
|
+
contents: write # Required for pushing the tag and creating the release
|
|
101
|
+
id-token: write # Required for provenance and trusted publishing
|
|
102
|
+
environment:
|
|
103
|
+
name: npm-publish
|
|
104
|
+
steps:
|
|
105
|
+
- name: Checkout
|
|
106
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
107
|
+
with:
|
|
108
|
+
fetch-depth: 0 # \`gh release create --generate-notes\` needs history
|
|
109
|
+
|
|
110
|
+
- name: Prepare for publishing
|
|
111
|
+
uses: ./.github/actions/publish-prepare
|
|
112
|
+
|
|
113
|
+
- name: Resolve version
|
|
114
|
+
id: version
|
|
115
|
+
# Refuses when the version is already on npm: main is meant to carry the
|
|
116
|
+
# next unreleased version, so finding it published means \`fg-dist
|
|
117
|
+
# release\` has not run since the last one.
|
|
118
|
+
run: pnpm run release:check
|
|
119
|
+
|
|
120
|
+
- name: Publish to npm
|
|
121
|
+
env:
|
|
122
|
+
DIST_TAG: \${{ inputs.dist-tag }}
|
|
123
|
+
# npm refuses provenance for anything not published publicly.
|
|
124
|
+
PROVENANCE: \${{ steps.version.outputs.provenance }}
|
|
125
|
+
DRY_RUN: \${{ inputs.dry-run }}
|
|
126
|
+
run: |
|
|
127
|
+
flags="--tag \$DIST_TAG"
|
|
128
|
+
[ "\$PROVENANCE" = "true" ] && flags="\$flags --provenance"
|
|
129
|
+
[ "\$DRY_RUN" = "true" ] && flags="\$flags --dry-run"
|
|
130
|
+
npm publish \$flags
|
|
131
|
+
|
|
132
|
+
- name: Tag and create the GitHub release
|
|
133
|
+
if: inputs.dry-run != true
|
|
134
|
+
env:
|
|
135
|
+
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
136
|
+
TAG: \${{ steps.version.outputs.tag }}
|
|
137
|
+
run: |
|
|
138
|
+
git tag "\$TAG"
|
|
139
|
+
git push origin "\$TAG"
|
|
140
|
+
gh release create "\$TAG" \\
|
|
141
|
+
--title "\$TAG" \\
|
|
142
|
+
--generate-notes \\
|
|
143
|
+
--verify-tag
|
|
144
|
+
|
|
145
|
+
- name: Summary
|
|
146
|
+
run: |
|
|
147
|
+
echo "Published \\\`${packageName}@\${{ steps.version.outputs.version }}\\\` to the \\\`\${{ inputs.dist-tag }}\\\` tag." >> "\$GITHUB_STEP_SUMMARY"
|
|
148
|
+
`,
|
|
149
|
+
'.github/actions/publish-prepare/action.yml': `name: Prepare for publishing
|
|
150
|
+
description: Install dependencies and build, so the package is ready to publish.
|
|
151
|
+
|
|
152
|
+
# Kept as an action rather than repeated steps: npm trusted publishing pins one
|
|
153
|
+
# workflow file per package, so the canary and release jobs both have to share
|
|
154
|
+
# this.
|
|
155
|
+
|
|
156
|
+
runs:
|
|
157
|
+
using: composite
|
|
158
|
+
steps:
|
|
159
|
+
# Pin these to a commit sha once Renovate is watching the repository. The
|
|
160
|
+
# checkout in the workflow is pinned already, because it handles credentials.
|
|
161
|
+
#
|
|
162
|
+
# No \`version\` here: the \`packageManager\` field in package.json is what
|
|
163
|
+
# pins pnpm, and giving both makes this action refuse to choose.
|
|
164
|
+
- uses: pnpm/action-setup@v4
|
|
165
|
+
|
|
166
|
+
- uses: actions/setup-node@v4
|
|
167
|
+
with:
|
|
168
|
+
# Inline rather than \`node-version-file\`, so this action works in a
|
|
169
|
+
# repository that has no \`.nvmrc\`.
|
|
170
|
+
node-version: 22
|
|
171
|
+
# No \`cache: pnpm\`: it needs a lockfile to hash, and a module developed
|
|
172
|
+
# inside a distribution keeps its lockfile in the distribution.
|
|
173
|
+
registry-url: https://registry.npmjs.org
|
|
174
|
+
|
|
175
|
+
- name: Install
|
|
176
|
+
shell: bash
|
|
177
|
+
run: |
|
|
178
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
179
|
+
pnpm install --frozen-lockfile
|
|
180
|
+
else
|
|
181
|
+
# A module developed inside a distribution has no lockfile of its
|
|
182
|
+
# own; the distribution's workspace holds it.
|
|
183
|
+
pnpm install --no-frozen-lockfile
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
- name: Build
|
|
187
|
+
shell: bash
|
|
188
|
+
# A module that is consumed as source has nothing to build; one that ships
|
|
189
|
+
# a \`dist\` does. Neither should have to edit this file.
|
|
190
|
+
run: pnpm run --if-present build
|
|
191
|
+
`,
|
|
192
|
+
});
|
|
193
|
+
const readManifest = async (root) => {
|
|
194
|
+
try {
|
|
195
|
+
return JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
/** The package whose CLI a module releases with. */
|
|
202
|
+
const TOOL = '@fairgarden/distribution';
|
|
203
|
+
/**
|
|
204
|
+
* This tool's own version, which is what a module is given to release with.
|
|
205
|
+
*
|
|
206
|
+
* Read rather than written down, so a module is pinned to the CLI that set it
|
|
207
|
+
* up instead of to whatever was current when this file was last edited.
|
|
208
|
+
*/
|
|
209
|
+
const toolVersion = async () => {
|
|
210
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
211
|
+
const own = await readManifest(path.join(here, '..'));
|
|
212
|
+
return typeof own?.version === 'string' ? own.version : undefined;
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* The scripts the workflow calls, and a module releases by hand with.
|
|
216
|
+
*
|
|
217
|
+
* They are named rather than spelled out in the workflow so that every
|
|
218
|
+
* module's workflow is the same file, and so `pnpm release` is the answer to
|
|
219
|
+
* "how do I release this" wherever you are standing.
|
|
220
|
+
*/
|
|
221
|
+
export const releaseScripts = (packageName) => {
|
|
222
|
+
// The tool cannot depend on itself being published to release itself.
|
|
223
|
+
const cli = packageName === TOOL ? 'node dist/cli.js' : 'fg-dist';
|
|
224
|
+
return {
|
|
225
|
+
canary: `${cli} canary`,
|
|
226
|
+
release: `${cli} release`,
|
|
227
|
+
'release:check': `${cli} release --check`,
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Give a module what it needs to publish itself.
|
|
232
|
+
*
|
|
233
|
+
* One pass over the manifest rather than several: a structural rewrite undoes
|
|
234
|
+
* a careful textual splice, so doing both would mean the second silently
|
|
235
|
+
* discarding the first's formatting. Two-space JSON is what npm and pnpm write
|
|
236
|
+
* anyway.
|
|
237
|
+
*/
|
|
238
|
+
export const updateManifest = async (moduleRoot, packageName, version, packageManager, { write = true } = {}) => {
|
|
239
|
+
const manifest = await readManifest(moduleRoot);
|
|
240
|
+
if (!manifest) {
|
|
241
|
+
return { changed: false, unprivated: false, missingFiles: false, missingAccess: false };
|
|
242
|
+
}
|
|
243
|
+
const before = JSON.stringify(manifest);
|
|
244
|
+
const updated = { ...manifest };
|
|
245
|
+
// A private package cannot be published, which is the one thing the workflow
|
|
246
|
+
// being added here is for.
|
|
247
|
+
const unprivated = updated.private === true;
|
|
248
|
+
if (unprivated)
|
|
249
|
+
delete updated.private;
|
|
250
|
+
// `pnpm/action-setup` takes its version from this field and will not run
|
|
251
|
+
// without one. A module that pins its own is left alone.
|
|
252
|
+
if (!updated.packageManager && packageManager) {
|
|
253
|
+
updated.packageManager = packageManager;
|
|
254
|
+
}
|
|
255
|
+
const scripts = { ...updated.scripts };
|
|
256
|
+
for (const [name, command] of Object.entries(releaseScripts(packageName))) {
|
|
257
|
+
// A script the module already defines may call the tool differently on
|
|
258
|
+
// purpose.
|
|
259
|
+
if (!scripts[name])
|
|
260
|
+
scripts[name] = command;
|
|
261
|
+
}
|
|
262
|
+
updated.scripts = scripts;
|
|
263
|
+
const dependencies = updated.dependencies;
|
|
264
|
+
const devDependencies = updated.devDependencies;
|
|
265
|
+
// Whichever list already has it wins; the tool is only needed to release, so
|
|
266
|
+
// a module that does not already depend on it gets it as a devDependency.
|
|
267
|
+
if (packageName !== TOOL && !dependencies?.[TOOL] && !devDependencies?.[TOOL] && version) {
|
|
268
|
+
updated.devDependencies = { ...devDependencies, [TOOL]: `^${version}` };
|
|
269
|
+
}
|
|
270
|
+
const changed = JSON.stringify(updated) !== before;
|
|
271
|
+
if (changed && write) {
|
|
272
|
+
await writeFile(path.join(moduleRoot, 'package.json'), `${JSON.stringify(updated, null, 2)}\n`);
|
|
273
|
+
}
|
|
274
|
+
// Guessed at, this would be wrong: a module consumed as source ships `app`
|
|
275
|
+
// and `lib`, one that builds ships `dist`, and npm's default ships both plus
|
|
276
|
+
// whatever else is lying around. Report it instead.
|
|
277
|
+
// Both of these are reported rather than guessed. `files` because a module
|
|
278
|
+
// consumed as source ships `app` and `lib` while one that builds ships
|
|
279
|
+
// `dist`. `publishConfig.access` because npm takes an explicit value as an
|
|
280
|
+
// instruction to *change* an existing package's visibility — writing
|
|
281
|
+
// `restricted` into an already-public package would make it private on the
|
|
282
|
+
// next publish, or fail on an org with no paid plan.
|
|
283
|
+
return {
|
|
284
|
+
changed,
|
|
285
|
+
unprivated,
|
|
286
|
+
missingFiles: !updated.files,
|
|
287
|
+
missingAccess: packageName.startsWith('@') &&
|
|
288
|
+
updated.publishConfig?.access === undefined,
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Give every module a publishing workflow.
|
|
293
|
+
*
|
|
294
|
+
* A module that already has one is left alone: it may have been changed on
|
|
295
|
+
* purpose, and overwriting it would be the kind of help nobody asked for.
|
|
296
|
+
*/
|
|
297
|
+
export const writeWorkflows = async (root, { force = false, dryRun = false } = {}) => {
|
|
298
|
+
const updates = [];
|
|
299
|
+
// What the distribution builds with, which is what its modules are developed
|
|
300
|
+
// against whether or not they say so themselves.
|
|
301
|
+
const packageManager = (await readManifest(root))?.packageManager;
|
|
302
|
+
const spec = typeof packageManager === 'string' ? packageManager : undefined;
|
|
303
|
+
const tool = await toolVersion();
|
|
304
|
+
for (const submodule of submodules(root)) {
|
|
305
|
+
const name = (await readManifest(submodule.path))?.name;
|
|
306
|
+
if (typeof name !== 'string')
|
|
307
|
+
continue;
|
|
308
|
+
const workflow = path.join(submodule.path, '.github/workflows/publish.yml');
|
|
309
|
+
if (existsSync(workflow) && !force) {
|
|
310
|
+
updates.push({
|
|
311
|
+
relativePath: submodule.relativePath,
|
|
312
|
+
files: [],
|
|
313
|
+
skipped: true,
|
|
314
|
+
unprivated: false,
|
|
315
|
+
missingFiles: false,
|
|
316
|
+
missingAccess: false,
|
|
317
|
+
});
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const written = [];
|
|
321
|
+
for (const [relative, contents] of Object.entries(publishWorkflow(name))) {
|
|
322
|
+
const full = path.join(submodule.path, relative);
|
|
323
|
+
if (!dryRun) {
|
|
324
|
+
await mkdir(path.dirname(full), { recursive: true });
|
|
325
|
+
await writeFile(full, contents);
|
|
326
|
+
}
|
|
327
|
+
written.push(relative);
|
|
328
|
+
}
|
|
329
|
+
// An earlier version of this command shipped the canary stamper as a file
|
|
330
|
+
// in the module. It is a CLI command now, and a stale copy of it would go
|
|
331
|
+
// on being the thing anyone reads.
|
|
332
|
+
const stale = path.join(submodule.path, 'scripts/stampCanaryVersion.mjs');
|
|
333
|
+
if (existsSync(stale)) {
|
|
334
|
+
if (!dryRun) {
|
|
335
|
+
await rm(stale);
|
|
336
|
+
// And the directory, if that was the only thing in it. A module with
|
|
337
|
+
// scripts of its own keeps them.
|
|
338
|
+
await rm(path.dirname(stale), { recursive: false }).catch(() => { });
|
|
339
|
+
}
|
|
340
|
+
written.push('scripts/stampCanaryVersion.mjs (removed)');
|
|
341
|
+
}
|
|
342
|
+
const manifest = await updateManifest(submodule.path, name, tool, spec, {
|
|
343
|
+
write: !dryRun,
|
|
344
|
+
});
|
|
345
|
+
if (manifest.changed)
|
|
346
|
+
written.push('package.json');
|
|
347
|
+
updates.push({
|
|
348
|
+
relativePath: submodule.relativePath,
|
|
349
|
+
files: written.sort(),
|
|
350
|
+
skipped: false,
|
|
351
|
+
unprivated: manifest.unprivated,
|
|
352
|
+
missingFiles: manifest.missingFiles,
|
|
353
|
+
missingAccess: manifest.missingAccess,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return updates;
|
|
357
|
+
};
|
|
358
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"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"}
|