@naturalcycles/dev-lib 20.52.2 → 20.53.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/dist/bin/dev-lib.js +5 -15
- package/dist/check.util.d.ts +2 -18
- package/dist/check.util.js +4 -69
- package/dist/release/git.util.d.ts +7 -5
- package/dist/release/git.util.js +46 -21
- package/dist/release/release.command.js +2 -2
- package/dist/release/version.util.js +4 -1
- package/package.json +3 -3
package/dist/bin/dev-lib.js
CHANGED
|
@@ -2,7 +2,7 @@ import { _by } from '@naturalcycles/js-lib/array/array.util.js';
|
|
|
2
2
|
import { _assert } from '@naturalcycles/js-lib/error/assert.js';
|
|
3
3
|
import { fs2 } from '@naturalcycles/nodejs-lib/fs2';
|
|
4
4
|
import { runScript } from '@naturalcycles/nodejs-lib/runScript';
|
|
5
|
-
import { buildCopy, buildProd, eslintAll, lintStagedCommand, runCheck, runOxfmt, runOxlint, runTest, stylelintAll,
|
|
5
|
+
import { buildCopy, buildProd, eslintAll, lintStagedCommand, runCheck, runOxfmt, runOxlint, runTest, stylelintAll, runTypecheck, } from '../check.util.js';
|
|
6
6
|
import { runCommitlint } from '../commitlint.command.js';
|
|
7
7
|
import { releaseCommand } from '../release/release.command.js';
|
|
8
8
|
const commands = [
|
|
@@ -15,23 +15,13 @@ const commands = [
|
|
|
15
15
|
{ name: 'bt', fn: bt, desc: 'Build & Test: run "typecheck" and then "test".' },
|
|
16
16
|
{
|
|
17
17
|
name: 'typecheck',
|
|
18
|
-
fn:
|
|
19
|
-
desc: 'Run typecheck
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'typecheck-with-tsc',
|
|
23
|
-
fn: typecheckWithTSC,
|
|
24
|
-
desc: 'Run typecheck (tsc) in folders (src, scripts, e2e) if there is tsconfig.json present.',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'typecheck-with-tsgo',
|
|
28
|
-
fn: typecheckWithTSGO,
|
|
29
|
-
desc: 'Run typecheck (tsgo) in folders (src, scripts, e2e) if there is tsconfig.json present.',
|
|
18
|
+
fn: runTypecheck,
|
|
19
|
+
desc: 'Run typecheck',
|
|
30
20
|
},
|
|
31
21
|
{
|
|
32
22
|
name: 'build',
|
|
33
23
|
fn: buildProd,
|
|
34
|
-
desc: 'Run "build-copy" then
|
|
24
|
+
desc: 'Run "build-copy" then tsc with --emit and --noCheck, using tsconfig.prod.json',
|
|
35
25
|
},
|
|
36
26
|
{
|
|
37
27
|
name: 'build-copy',
|
|
@@ -170,7 +160,7 @@ async function quickCheck() {
|
|
|
170
160
|
async function bt() {
|
|
171
161
|
// Still using ts, as oxlint is found to fail in certain cases
|
|
172
162
|
// await typecheckWithOxlint()
|
|
173
|
-
await
|
|
163
|
+
await runTypecheck();
|
|
174
164
|
runTest();
|
|
175
165
|
}
|
|
176
166
|
// async function _typecheckWithOxlint(): Promise<void> {
|
package/dist/check.util.d.ts
CHANGED
|
@@ -9,13 +9,7 @@ export interface CheckOptions {
|
|
|
9
9
|
stylelint?: boolean;
|
|
10
10
|
oxfmt?: boolean;
|
|
11
11
|
ktlint?: boolean;
|
|
12
|
-
|
|
13
|
-
* true - run tsgo, otherwise tsc
|
|
14
|
-
* tsgo - run tsgo
|
|
15
|
-
* tsc - run tsc
|
|
16
|
-
* false - skip
|
|
17
|
-
*/
|
|
18
|
-
typecheck?: boolean | 'tsc' | 'tsgo';
|
|
12
|
+
typecheck?: boolean;
|
|
19
13
|
test?: boolean;
|
|
20
14
|
}
|
|
21
15
|
/**
|
|
@@ -53,22 +47,12 @@ export declare function lintStagedCommand(): Promise<void>;
|
|
|
53
47
|
export declare function requireActionlintVersion(): void;
|
|
54
48
|
export declare function getActionLintVersion(): SemVerString | undefined;
|
|
55
49
|
export declare function buildProd(): void;
|
|
56
|
-
|
|
57
|
-
* Uses tsgo if it's installed, otherwise tsc
|
|
58
|
-
*/
|
|
59
|
-
export declare function typecheckWithTS(): Promise<void>;
|
|
60
|
-
export declare function typecheckWithTSC(): Promise<void>;
|
|
61
|
-
export declare function typecheckWithTSGO(): Promise<void>;
|
|
50
|
+
export declare function runTypecheck(): Promise<void>;
|
|
62
51
|
/**
|
|
63
52
|
* Use 'src' to indicate root.
|
|
64
53
|
*/
|
|
65
54
|
export declare function runTSCInFolders(dirs: string[], args?: string[], parallel?: boolean): Promise<void>;
|
|
66
|
-
/**
|
|
67
|
-
* Use 'src' to indicate root.
|
|
68
|
-
*/
|
|
69
|
-
export declare function runTSGOInFolders(dirs: string[], args?: string[]): Promise<void>;
|
|
70
55
|
export declare function runTSCProd(args?: string[]): void;
|
|
71
|
-
export declare function runTSGOProd(args?: string[]): void;
|
|
72
56
|
export declare function buildCopy(): void;
|
|
73
57
|
interface RunTestOptions {
|
|
74
58
|
integration?: boolean;
|
package/dist/check.util.js
CHANGED
|
@@ -88,18 +88,8 @@ export async function runCheck(opt = {}) {
|
|
|
88
88
|
}
|
|
89
89
|
if (typecheck) {
|
|
90
90
|
s = Date.now();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
timings['tsgo'] = Date.now() - s;
|
|
94
|
-
}
|
|
95
|
-
else if (typecheck === 'tsc') {
|
|
96
|
-
await typecheckWithTSC();
|
|
97
|
-
timings['tsc'] = Date.now() - s;
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
await typecheckWithTS();
|
|
101
|
-
timings['typecheck'] = Date.now() - s;
|
|
102
|
-
}
|
|
91
|
+
await runTypecheck();
|
|
92
|
+
timings['tsc'] = Date.now() - s;
|
|
103
93
|
}
|
|
104
94
|
if (test) {
|
|
105
95
|
s = Date.now();
|
|
@@ -322,30 +312,11 @@ export function getActionLintVersion() {
|
|
|
322
312
|
export function buildProd() {
|
|
323
313
|
// fs2.emptyDir('./dist') // it doesn't delete the dir itself, to prevent IDE jumping
|
|
324
314
|
buildCopy();
|
|
325
|
-
|
|
326
|
-
// now we trust tsgo to run prod builds
|
|
327
|
-
runTSGOProd();
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
runTSCProd();
|
|
331
|
-
}
|
|
315
|
+
runTSCProd();
|
|
332
316
|
}
|
|
333
|
-
|
|
334
|
-
* Uses tsgo if it's installed, otherwise tsc
|
|
335
|
-
*/
|
|
336
|
-
export async function typecheckWithTS() {
|
|
337
|
-
if (hasDependencyInNodeModules('@typescript/native-preview')) {
|
|
338
|
-
await typecheckWithTSGO();
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
await typecheckWithTSC();
|
|
342
|
-
}
|
|
343
|
-
export async function typecheckWithTSC() {
|
|
317
|
+
export async function runTypecheck() {
|
|
344
318
|
await runTSCInFolders(['src', 'scripts', 'e2e'], ['--noEmit']);
|
|
345
319
|
}
|
|
346
|
-
export async function typecheckWithTSGO() {
|
|
347
|
-
await runTSGOInFolders(['src', 'scripts', 'e2e'], ['--noEmit', '--incremental', 'false']);
|
|
348
|
-
}
|
|
349
320
|
/**
|
|
350
321
|
* Use 'src' to indicate root.
|
|
351
322
|
*/
|
|
@@ -381,34 +352,6 @@ async function runTSCInFolder(dir, args = []) {
|
|
|
381
352
|
shell: false,
|
|
382
353
|
});
|
|
383
354
|
}
|
|
384
|
-
/**
|
|
385
|
-
* Use 'src' to indicate root.
|
|
386
|
-
*/
|
|
387
|
-
export async function runTSGOInFolders(dirs, args = []) {
|
|
388
|
-
// Run sequential, since tsgo (unlike tsc) uses all cpu cores already
|
|
389
|
-
for (const dir of dirs) {
|
|
390
|
-
await runTSGOInFolder(dir, args);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Pass 'src' to run in root.
|
|
395
|
-
*/
|
|
396
|
-
async function runTSGOInFolder(dir, args = []) {
|
|
397
|
-
let configDir = dir;
|
|
398
|
-
if (dir === 'src') {
|
|
399
|
-
configDir = '';
|
|
400
|
-
}
|
|
401
|
-
const tsconfigPath = [configDir, 'tsconfig.json'].filter(Boolean).join('/');
|
|
402
|
-
if (!fs2.pathExists(tsconfigPath) || !fs2.pathExists(dir)) {
|
|
403
|
-
// console.log(`Skipping to run tsgo for ${tsconfigPath}, as it doesn't exist`)
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
const tsgoPath = findPackageBinPath('@typescript/native-preview', 'tsgo');
|
|
407
|
-
await exec2.spawnAsync(tsgoPath, {
|
|
408
|
-
args: ['-P', tsconfigPath, ...args],
|
|
409
|
-
shell: false,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
355
|
export function runTSCProd(args = []) {
|
|
413
356
|
const tsconfigPath = [`./tsconfig.prod.json`].find(p => fs2.pathExists(p)) || 'tsconfig.json';
|
|
414
357
|
const tscPath = findPackageBinPath('typescript', 'tsc');
|
|
@@ -417,14 +360,6 @@ export function runTSCProd(args = []) {
|
|
|
417
360
|
shell: false,
|
|
418
361
|
});
|
|
419
362
|
}
|
|
420
|
-
export function runTSGOProd(args = []) {
|
|
421
|
-
const tsconfigPath = [`./tsconfig.prod.json`].find(p => fs2.pathExists(p)) || 'tsconfig.json';
|
|
422
|
-
const tsgoPath = findPackageBinPath('@typescript/native-preview', 'tsgo');
|
|
423
|
-
exec2.spawn(tsgoPath, {
|
|
424
|
-
args: ['-P', tsconfigPath, '--noEmit', 'false', '--noCheck', '--incremental', 'false', ...args],
|
|
425
|
-
shell: false,
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
363
|
export function buildCopy() {
|
|
429
364
|
const baseDir = 'src';
|
|
430
365
|
const inputPatterns = [
|
|
@@ -8,14 +8,18 @@ export declare function getCurrentBranch(): string;
|
|
|
8
8
|
/**
|
|
9
9
|
* Make sure the full git history and all tags are available:
|
|
10
10
|
* CI checkouts are often shallow and without tags, while release calculation is based on tags.
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
|
+
* On a complete (non-shallow) clone a failed fetch is tolerated with a warning (e.g. offline
|
|
13
|
+
* local dry-run) - the local tags are complete enough. On a shallow clone a failed unshallow
|
|
14
|
+
* is fatal: a truncated history hides release tags and would make a released package look like
|
|
15
|
+
* a first release, publishing a wrong version.
|
|
12
16
|
*/
|
|
13
|
-
export declare function ensureFullGitHistory(): void;
|
|
17
|
+
export declare function ensureFullGitHistory(repo: RepoInfo): void;
|
|
14
18
|
/**
|
|
15
19
|
* Abort if someone pushed to the branch since this checkout - releasing from a stale HEAD
|
|
16
20
|
* would compute wrong versions/notes. No-op if the branch has no remote counterpart.
|
|
17
21
|
*/
|
|
18
|
-
export declare function checkHeadIsUpToDateWithRemote(branch: string): void;
|
|
22
|
+
export declare function checkHeadIsUpToDateWithRemote(branch: string, repo: RepoInfo): void;
|
|
19
23
|
/**
|
|
20
24
|
* All git tags reachable from HEAD.
|
|
21
25
|
* Reachability matters: on a prerelease branch this naturally excludes tags of other
|
|
@@ -29,7 +33,5 @@ export declare function getReachableTags(): string[];
|
|
|
29
33
|
export declare function getCommitsSince(tag: string | undefined, relativeDir?: string): RawCommit[];
|
|
30
34
|
/**
|
|
31
35
|
* Create the release tag on HEAD and push it to origin.
|
|
32
|
-
* Uses a token-authenticated url when GITHUB_TOKEN is available,
|
|
33
|
-
* since CI checkouts don't persist git credentials.
|
|
34
36
|
*/
|
|
35
37
|
export declare function createAndPushTag(tag: string, repo: RepoInfo): void;
|
package/dist/release/git.util.js
CHANGED
|
@@ -24,33 +24,52 @@ export function getCurrentBranch() {
|
|
|
24
24
|
/**
|
|
25
25
|
* Make sure the full git history and all tags are available:
|
|
26
26
|
* CI checkouts are often shallow and without tags, while release calculation is based on tags.
|
|
27
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* On a complete (non-shallow) clone a failed fetch is tolerated with a warning (e.g. offline
|
|
29
|
+
* local dry-run) - the local tags are complete enough. On a shallow clone a failed unshallow
|
|
30
|
+
* is fatal: a truncated history hides release tags and would make a released package look like
|
|
31
|
+
* a first release, publishing a wrong version.
|
|
28
32
|
*/
|
|
29
|
-
export function ensureFullGitHistory() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
export function ensureFullGitHistory(repo) {
|
|
34
|
+
// Fetch from an authenticated url, not `origin`: private repos reject unauthenticated fetches,
|
|
35
|
+
// and CI checkouts don't persist git credentials (persist-credentials: false).
|
|
36
|
+
const remote = getAuthenticatedRemote(repo);
|
|
37
|
+
const shallow = exec2.exec('git rev-parse --is-shallow-repository').trim() === 'true';
|
|
38
|
+
if (shallow) {
|
|
39
|
+
console.log(dimGrey('git fetch --unshallow --tags'));
|
|
40
|
+
try {
|
|
41
|
+
exec2.exec(`git fetch --unshallow --tags --quiet "${remote}"`);
|
|
34
42
|
}
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
catch {
|
|
44
|
+
// Sanitized: the original error contains the failed command incl. the token
|
|
45
|
+
throw new Error('git fetch --unshallow failed - refusing to release with a shallow git history, as version calculation depends on it');
|
|
37
46
|
}
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
console.log(dimGrey(
|
|
48
|
+
else {
|
|
49
|
+
console.log(dimGrey('git fetch --tags'));
|
|
50
|
+
try {
|
|
51
|
+
exec2.exec(`git fetch --tags --quiet "${remote}"`);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
console.log(dimGrey('git fetch --tags failed, continuing with local tags/history'));
|
|
55
|
+
}
|
|
41
56
|
}
|
|
42
57
|
}
|
|
43
58
|
/**
|
|
44
59
|
* Abort if someone pushed to the branch since this checkout - releasing from a stale HEAD
|
|
45
60
|
* would compute wrong versions/notes. No-op if the branch has no remote counterpart.
|
|
46
61
|
*/
|
|
47
|
-
export function checkHeadIsUpToDateWithRemote(branch) {
|
|
62
|
+
export function checkHeadIsUpToDateWithRemote(branch, repo) {
|
|
63
|
+
const remote = getAuthenticatedRemote(repo);
|
|
48
64
|
let remoteSha;
|
|
49
65
|
try {
|
|
50
|
-
remoteSha = exec2
|
|
66
|
+
remoteSha = exec2
|
|
67
|
+
.exec(`git ls-remote "${remote}" "refs/heads/${branch}"`)
|
|
68
|
+
.split('\t')[0]
|
|
69
|
+
?.trim();
|
|
51
70
|
}
|
|
52
|
-
catch
|
|
53
|
-
console.log(dimGrey(
|
|
71
|
+
catch {
|
|
72
|
+
console.log(dimGrey('git ls-remote failed, skipping up-to-date check'));
|
|
54
73
|
return;
|
|
55
74
|
}
|
|
56
75
|
if (!remoteSha)
|
|
@@ -91,20 +110,26 @@ export function getCommitsSince(tag, relativeDir) {
|
|
|
91
110
|
}
|
|
92
111
|
/**
|
|
93
112
|
* Create the release tag on HEAD and push it to origin.
|
|
94
|
-
* Uses a token-authenticated url when GITHUB_TOKEN is available,
|
|
95
|
-
* since CI checkouts don't persist git credentials.
|
|
96
113
|
*/
|
|
97
114
|
export function createAndPushTag(tag, repo) {
|
|
98
115
|
exec2.exec(`git tag "${tag}"`);
|
|
99
|
-
const token = process.env['GITHUB_TOKEN'] || process.env['GH_TOKEN'];
|
|
100
|
-
const pushUrl = token
|
|
101
|
-
? `https://x-access-token:${token}@github.com/${repo.owner}/${repo.repo}.git`
|
|
102
|
-
: 'origin';
|
|
103
116
|
try {
|
|
104
|
-
exec2.exec(`git push "${
|
|
117
|
+
exec2.exec(`git push "${getAuthenticatedRemote(repo)}" "refs/tags/${tag}"`);
|
|
105
118
|
}
|
|
106
119
|
catch {
|
|
107
120
|
// Sanitized rethrow: the original error message contains the failed command incl. the token
|
|
108
121
|
throw new Error(`Failed to push tag ${tag} to origin`);
|
|
109
122
|
}
|
|
110
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Token-authenticated remote url when GITHUB_TOKEN is available, `origin` otherwise.
|
|
126
|
+
* CI checkouts don't persist git credentials (persist-credentials: false), so all remote
|
|
127
|
+
* git operations (fetch, ls-remote, push) go through this.
|
|
128
|
+
* NEVER log commands containing this url, and sanitize their errors - they carry the token.
|
|
129
|
+
*/
|
|
130
|
+
function getAuthenticatedRemote(repo) {
|
|
131
|
+
const token = process.env['GITHUB_TOKEN'] || process.env['GH_TOKEN'];
|
|
132
|
+
return token
|
|
133
|
+
? `https://x-access-token:${token}@github.com/${repo.owner}/${repo.repo}.git`
|
|
134
|
+
: 'origin';
|
|
135
|
+
}
|
|
@@ -50,9 +50,9 @@ export async function releaseCommand() {
|
|
|
50
50
|
if (!dryRun) {
|
|
51
51
|
_assert(token, 'GITHUB_TOKEN (or GH_TOKEN) is required to release, or use --dry-run');
|
|
52
52
|
}
|
|
53
|
-
ensureFullGitHistory();
|
|
53
|
+
ensureFullGitHistory(repo);
|
|
54
54
|
if (!dryRun) {
|
|
55
|
-
checkHeadIsUpToDateWithRemote(branch);
|
|
55
|
+
checkHeadIsUpToDateWithRemote(branch, repo);
|
|
56
56
|
}
|
|
57
57
|
const mode = resolveReleaseMode(cfg.mode);
|
|
58
58
|
const packages = discoverReleasePackages(mode, argv.filter);
|
|
@@ -49,9 +49,12 @@ export function getNextVersion(input) {
|
|
|
49
49
|
const { prereleaseId } = channel;
|
|
50
50
|
if (!lastRelease) {
|
|
51
51
|
const { firstReleaseVersion } = input;
|
|
52
|
+
// 0.0.0 is the "version is managed by git tags" placeholder, never a real first release
|
|
52
53
|
const first = (firstReleaseVersion &&
|
|
53
54
|
!semver.prerelease(firstReleaseVersion) &&
|
|
54
|
-
semver.valid(firstReleaseVersion)
|
|
55
|
+
semver.valid(firstReleaseVersion) &&
|
|
56
|
+
semver.gt(firstReleaseVersion, '0.0.0') &&
|
|
57
|
+
firstReleaseVersion) ||
|
|
55
58
|
'1.0.0';
|
|
56
59
|
return prereleaseId ? `${first}-${prereleaseId}.1` : first;
|
|
57
60
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/dev-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "20.
|
|
4
|
+
"version": "20.53.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"prompts": "^2",
|
|
7
7
|
"@naturalcycles/js-lib": "^15",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@eslint/js": "^10",
|
|
22
|
-
"typescript": "^7
|
|
22
|
+
"typescript": "^7",
|
|
23
23
|
"eslint": "^10",
|
|
24
24
|
"husky": "^9",
|
|
25
25
|
"stylelint": "^17",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@types/picomatch": "^4",
|
|
52
52
|
"@types/prompts": "^2",
|
|
53
53
|
"@types/semver": "^7",
|
|
54
|
-
"typescript": "
|
|
54
|
+
"typescript": "^7"
|
|
55
55
|
},
|
|
56
56
|
"exports": {
|
|
57
57
|
"./cfg/": "./cfg/",
|