@smoothbricks/cli 0.1.0 → 0.1.1
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 +208 -83
- package/dist/cli.js +50 -20
- package/dist/lib/devenv.d.ts +6 -0
- package/dist/lib/devenv.d.ts.map +1 -0
- package/dist/lib/devenv.js +82 -0
- package/dist/lib/workspace.d.ts +5 -0
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +62 -5
- package/dist/monorepo/index.d.ts +6 -1
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +38 -5
- package/dist/monorepo/lockfile.d.ts +5 -1
- package/dist/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +45 -5
- package/dist/monorepo/managed-files.d.ts +1 -1
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +27 -10
- package/dist/monorepo/package-hygiene.js +1 -1
- package/dist/monorepo/package-policy.d.ts +3 -0
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +122 -19
- package/dist/monorepo/packed-manifest.d.ts +4 -0
- package/dist/monorepo/packed-manifest.d.ts.map +1 -0
- package/dist/monorepo/packed-manifest.js +51 -0
- package/dist/monorepo/packed-package.d.ts.map +1 -1
- package/dist/monorepo/packed-package.js +6 -0
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +4 -1
- package/dist/monorepo/publish-workflow.d.ts +86 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -0
- package/dist/monorepo/publish-workflow.js +343 -0
- package/dist/nx-version-actions.cjs +25 -0
- package/dist/nx-version-actions.d.cts +6 -0
- package/dist/nx-version-actions.d.cts.map +1 -0
- package/dist/release/core.d.ts +50 -0
- package/dist/release/core.d.ts.map +1 -0
- package/dist/release/core.js +97 -0
- package/dist/release/github-release.d.ts +46 -0
- package/dist/release/github-release.d.ts.map +1 -0
- package/dist/release/github-release.js +97 -0
- package/dist/release/index.d.ts +14 -5
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +544 -109
- package/dist/release/npm-auth.d.ts +17 -0
- package/dist/release/npm-auth.d.ts.map +1 -0
- package/dist/release/npm-auth.js +51 -0
- package/dist/release/orchestration.d.ts +48 -0
- package/dist/release/orchestration.d.ts.map +1 -0
- package/dist/release/orchestration.js +105 -0
- package/dist/release/publish-plan.d.ts +17 -0
- package/dist/release/publish-plan.d.ts.map +1 -0
- package/dist/release/publish-plan.js +15 -0
- package/dist/release/retag-unpublished.d.ts +34 -0
- package/dist/release/retag-unpublished.d.ts.map +1 -0
- package/dist/release/retag-unpublished.js +77 -0
- package/managed/raw/tooling/git-hooks/pre-commit.sh +2 -7
- package/managed/templates/github/workflows/ci.yml +2 -0
- package/package.json +24 -3
- package/src/cli.ts +64 -25
- package/src/lib/devenv.test.ts +28 -0
- package/src/lib/devenv.ts +89 -0
- package/src/lib/workspace.ts +76 -5
- package/src/monorepo/__tests__/nx-version-actions.test.ts +75 -0
- package/src/monorepo/__tests__/publish-workflow.test.ts +315 -0
- package/src/monorepo/index.ts +45 -5
- package/src/monorepo/lockfile.ts +52 -7
- package/src/monorepo/managed-files.ts +42 -12
- package/src/monorepo/package-hygiene.ts +1 -1
- package/src/monorepo/package-policy.ts +134 -18
- package/src/monorepo/packed-manifest.ts +67 -0
- package/src/monorepo/packed-package.ts +7 -0
- package/src/monorepo/packs/index.ts +4 -0
- package/src/monorepo/publish-workflow.ts +430 -0
- package/src/nx-version-actions.cts +36 -0
- package/src/release/__tests__/core-properties.test.ts +149 -0
- package/src/release/__tests__/core-scenarios.test.ts +177 -0
- package/src/release/__tests__/core.test.ts +73 -0
- package/src/release/__tests__/fixture-repo.test.ts +305 -0
- package/src/release/__tests__/github-release.test.ts +144 -0
- package/src/release/__tests__/helpers/fixture-repo.ts +113 -0
- package/src/release/__tests__/npm-auth.test.ts +129 -0
- package/src/release/__tests__/orchestration.test.ts +202 -0
- package/src/release/__tests__/publish-plan.test.ts +61 -0
- package/src/release/__tests__/retag-unpublished.test.ts +160 -0
- package/src/release/core.ts +170 -0
- package/src/release/github-release.ts +134 -0
- package/src/release/index.ts +662 -116
- package/src/release/npm-auth.ts +122 -0
- package/src/release/orchestration.ts +186 -0
- package/src/release/publish-plan.ts +37 -0
- package/src/release/retag-unpublished.ts +122 -0
- package/managed/templates/github/workflows/publish.yml +0 -136
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { $ } from 'bun';
|
|
5
|
+
import { decode } from './run.js';
|
|
6
|
+
export async function withDevenvEnv(root, runWithEnv) {
|
|
7
|
+
const snapshot = snapshotProcessEnv();
|
|
8
|
+
const env = await loadDevenvEnv(root);
|
|
9
|
+
replaceProcessEnv(mergeDevenvEnv(snapshot, env));
|
|
10
|
+
try {
|
|
11
|
+
return await runWithEnv();
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
restoreProcessEnv(snapshot);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function loadDevenvEnv(root) {
|
|
18
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'smoo-devenv-env-'));
|
|
19
|
+
const envPath = join(tempDir, 'env');
|
|
20
|
+
try {
|
|
21
|
+
const result = await $ `devenv shell -- bash -lc ${'env -0 > "$1"'} bash ${envPath}`
|
|
22
|
+
.cwd(join(root, 'tooling', 'direnv'))
|
|
23
|
+
.quiet()
|
|
24
|
+
.nothrow();
|
|
25
|
+
if (result.exitCode !== 0) {
|
|
26
|
+
throw new Error('devenv shell failed. Ensure devenv is installed and the tooling/direnv shell is valid.');
|
|
27
|
+
}
|
|
28
|
+
return parseNulEnv(await readFile(envPath));
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function parseNulEnv(bytes) {
|
|
35
|
+
const env = {};
|
|
36
|
+
let entryStart = 0;
|
|
37
|
+
for (let index = 0; index <= bytes.length; index += 1) {
|
|
38
|
+
if (index !== bytes.length && bytes[index] !== 0) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (index === entryStart) {
|
|
42
|
+
entryStart = index + 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const entry = decode(bytes.subarray(entryStart, index));
|
|
46
|
+
const separator = entry.indexOf('=');
|
|
47
|
+
if (separator <= 0) {
|
|
48
|
+
throw new Error('devenv shell produced an invalid environment entry.');
|
|
49
|
+
}
|
|
50
|
+
env[entry.slice(0, separator)] = entry.slice(separator + 1);
|
|
51
|
+
entryStart = index + 1;
|
|
52
|
+
}
|
|
53
|
+
return env;
|
|
54
|
+
}
|
|
55
|
+
export function mergeDevenvEnv(baseEnv, devenvEnv) {
|
|
56
|
+
return { ...baseEnv, ...devenvEnv };
|
|
57
|
+
}
|
|
58
|
+
function snapshotProcessEnv() {
|
|
59
|
+
const snapshot = {};
|
|
60
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
61
|
+
if (value !== undefined) {
|
|
62
|
+
snapshot[key] = value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return snapshot;
|
|
66
|
+
}
|
|
67
|
+
function replaceProcessEnv(env) {
|
|
68
|
+
for (const key of Object.keys(process.env)) {
|
|
69
|
+
delete process.env[key];
|
|
70
|
+
}
|
|
71
|
+
for (const [key, value] of Object.entries(env)) {
|
|
72
|
+
process.env[key] = value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function restoreProcessEnv(snapshot) {
|
|
76
|
+
for (const key of Object.keys(process.env)) {
|
|
77
|
+
delete process.env[key];
|
|
78
|
+
}
|
|
79
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
80
|
+
process.env[key] = value;
|
|
81
|
+
}
|
|
82
|
+
}
|
package/dist/lib/workspace.d.ts
CHANGED
|
@@ -13,11 +13,16 @@ export interface RepositoryInfo {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const workspaceDependencyFields: readonly ["dependencies", "devDependencies", "optionalDependencies"];
|
|
15
15
|
export declare function listPublicPackages(root: string): PackageInfo[];
|
|
16
|
+
export declare function listReleasePackages(root: string, rootPackage?: PackageInfo | null): PackageInfo[];
|
|
17
|
+
export declare function rootRepositoryInfo(root: string): RepositoryInfo | null;
|
|
18
|
+
export declare function packageRepositoryInfo(pkg: PackageInfo): RepositoryInfo | null;
|
|
19
|
+
export declare function isOwnedPackage(rootRepository: RepositoryInfo, pkg: PackageInfo): boolean;
|
|
16
20
|
export declare function getWorkspacePackages(root: string): PackageInfo[];
|
|
17
21
|
export declare function listPackageJsonRecords(root: string): PackageInfo[];
|
|
18
22
|
export declare function getWorkspacePatterns(root: string): string[];
|
|
19
23
|
export declare function readPackageJson(path: string): PackageInfo | null;
|
|
20
24
|
export declare function getNxTags(pkg: Record<string, unknown>): string[];
|
|
21
25
|
export declare function repositoryInfo(pkg: Record<string, unknown>): RepositoryInfo | null;
|
|
26
|
+
export declare function sameRepositoryAfterNormalization(left: string, right: string): boolean;
|
|
22
27
|
export declare function escapeRegex(value: string): string;
|
|
23
28
|
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/lib/workspace.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,yBAAyB,sEAAuE,CAAC;AAE9G,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAE9D;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/lib/workspace.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,yBAAyB,sEAAuE,CAAC;AAE9G,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAE9D;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,WAAW,qBAA8C,GACxD,WAAW,EAAE,CAWf;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAGtE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,GAAG,IAAI,CAE7E;AAED,wBAAgB,cAAc,CAAC,cAAc,EAAE,cAAc,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAGxF;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAMhE;AAgCD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CASlE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG3D;AAgBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAgBhE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAKhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,GAAG,IAAI,CAalF;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAErF;AA4BD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|
package/dist/lib/workspace.js
CHANGED
|
@@ -5,11 +5,35 @@ export const workspaceDependencyFields = ['dependencies', 'devDependencies', 'op
|
|
|
5
5
|
export function listPublicPackages(root) {
|
|
6
6
|
return getWorkspacePackages(root).filter((pkg) => !pkg.private && pkg.tags.includes('npm:public'));
|
|
7
7
|
}
|
|
8
|
+
export function listReleasePackages(root, rootPackage = readPackageJson(join(root, 'package.json'))) {
|
|
9
|
+
if (!rootPackage) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const rootRepository = repositoryInfo(rootPackage.json);
|
|
13
|
+
if (!rootRepository) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
return getWorkspacePackagesForPatterns(root, getWorkspacePatternsFromPackageJson(rootPackage.json)).filter((pkg) => !pkg.private && pkg.tags.includes('npm:public') && isOwnedPackage(rootRepository, pkg));
|
|
17
|
+
}
|
|
18
|
+
export function rootRepositoryInfo(root) {
|
|
19
|
+
const rootPackage = readJsonObject(join(root, 'package.json'));
|
|
20
|
+
return rootPackage ? repositoryInfo(rootPackage) : null;
|
|
21
|
+
}
|
|
22
|
+
export function packageRepositoryInfo(pkg) {
|
|
23
|
+
return repositoryInfo(pkg.json);
|
|
24
|
+
}
|
|
25
|
+
export function isOwnedPackage(rootRepository, pkg) {
|
|
26
|
+
const packageRepository = packageRepositoryInfo(pkg);
|
|
27
|
+
return packageRepository !== null && packageRepository.url === rootRepository.url;
|
|
28
|
+
}
|
|
8
29
|
export function getWorkspacePackages(root) {
|
|
9
|
-
|
|
30
|
+
const rootPackage = readPackageJson(join(root, 'package.json'));
|
|
31
|
+
if (!rootPackage) {
|
|
10
32
|
throw new Error('package.json not found or invalid');
|
|
11
33
|
}
|
|
12
|
-
|
|
34
|
+
return getWorkspacePackagesForPatterns(root, getWorkspacePatternsFromPackageJson(rootPackage.json));
|
|
35
|
+
}
|
|
36
|
+
function getWorkspacePackagesForPatterns(root, workspacePatterns) {
|
|
13
37
|
const packages = [];
|
|
14
38
|
for (const pattern of workspacePatterns) {
|
|
15
39
|
if (!pattern.endsWith('/*')) {
|
|
@@ -43,14 +67,20 @@ export function listPackageJsonRecords(root) {
|
|
|
43
67
|
if (!rootPackage) {
|
|
44
68
|
throw new Error('package.json not found or invalid');
|
|
45
69
|
}
|
|
46
|
-
return [
|
|
70
|
+
return [
|
|
71
|
+
{ ...rootPackage, path: '.' },
|
|
72
|
+
...getWorkspacePackagesForPatterns(root, getWorkspacePatternsFromPackageJson(rootPackage.json)),
|
|
73
|
+
];
|
|
47
74
|
}
|
|
48
75
|
export function getWorkspacePatterns(root) {
|
|
49
76
|
const raw = readJson(join(root, 'package.json'));
|
|
50
|
-
|
|
77
|
+
return isRecord(raw) ? getWorkspacePatternsFromPackageJson(raw) : ['packages/*'];
|
|
78
|
+
}
|
|
79
|
+
function getWorkspacePatternsFromPackageJson(pkg) {
|
|
80
|
+
if (!hasOwn(pkg, 'workspaces')) {
|
|
51
81
|
return ['packages/*'];
|
|
52
82
|
}
|
|
53
|
-
const workspaces =
|
|
83
|
+
const workspaces = pkg.workspaces;
|
|
54
84
|
if (Array.isArray(workspaces)) {
|
|
55
85
|
return workspaces.filter((entry) => typeof entry === 'string');
|
|
56
86
|
}
|
|
@@ -96,6 +126,33 @@ export function repositoryInfo(pkg) {
|
|
|
96
126
|
}
|
|
97
127
|
return { type: stringProperty(repository, 'type') ?? 'git', url };
|
|
98
128
|
}
|
|
129
|
+
export function sameRepositoryAfterNormalization(left, right) {
|
|
130
|
+
return normalizedRepositoryUrl(left) === normalizedRepositoryUrl(right);
|
|
131
|
+
}
|
|
132
|
+
function normalizedRepositoryUrl(url) {
|
|
133
|
+
const trimmed = url
|
|
134
|
+
.trim()
|
|
135
|
+
.replace(/^git\+/, '')
|
|
136
|
+
.replace(/#.*$/, '')
|
|
137
|
+
.replace(/\/$/, '')
|
|
138
|
+
.replace(/\.git$/, '');
|
|
139
|
+
const https = /^https:\/\/github\.com\/([^/]+)\/([^/]+)$/i.exec(trimmed);
|
|
140
|
+
if (https?.[1] && https[2]) {
|
|
141
|
+
return githubRepositoryKey(https[1], https[2]);
|
|
142
|
+
}
|
|
143
|
+
const ssh = /^git@github\.com:([^/]+)\/([^/]+)$/i.exec(trimmed);
|
|
144
|
+
if (ssh?.[1] && ssh[2]) {
|
|
145
|
+
return githubRepositoryKey(ssh[1], ssh[2]);
|
|
146
|
+
}
|
|
147
|
+
const shorthand = /^github:([^/]+)\/([^/]+)$/i.exec(trimmed);
|
|
148
|
+
if (shorthand?.[1] && shorthand[2]) {
|
|
149
|
+
return githubRepositoryKey(shorthand[1], shorthand[2]);
|
|
150
|
+
}
|
|
151
|
+
return trimmed;
|
|
152
|
+
}
|
|
153
|
+
function githubRepositoryKey(owner, repo) {
|
|
154
|
+
return `github:${owner.toLowerCase()}/${repo.toLowerCase()}`;
|
|
155
|
+
}
|
|
99
156
|
export function escapeRegex(value) {
|
|
100
157
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
101
158
|
}
|
package/dist/monorepo/index.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export interface InitOptions {
|
|
|
6
6
|
}
|
|
7
7
|
export interface ValidateOptions {
|
|
8
8
|
failFast?: boolean;
|
|
9
|
+
onlyIfNewWorkspacePackage?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ListReleasePackagesOptions {
|
|
12
|
+
failEmpty?: boolean;
|
|
13
|
+
githubOutput?: string;
|
|
9
14
|
}
|
|
10
15
|
export declare function initMonorepo(root: string, options: InitOptions): Promise<void>;
|
|
11
16
|
export declare function validateMonorepo(root: string, options?: ValidateOptions): Promise<void>;
|
|
@@ -13,7 +18,7 @@ export declare function updateManagedFiles(root: string): void;
|
|
|
13
18
|
export declare function checkManagedFiles(root: string): void;
|
|
14
19
|
export declare function diffManagedFiles(root: string): void;
|
|
15
20
|
export declare function validateCommitMessageFile(path: string | undefined): void;
|
|
16
|
-
export declare function
|
|
21
|
+
export declare function listReleasePackagesForNx(root: string, options?: ListReleasePackagesOptions): string;
|
|
17
22
|
export declare function validatePublicPackageTags(root: string): void;
|
|
18
23
|
export { applyWorkspaceGitConfig, syncBunLockfileVersions };
|
|
19
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/monorepo/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/monorepo/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAMxD,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpF;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CASxE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAWvG;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,CAAC"}
|
package/dist/monorepo/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
1
|
+
import { appendFileSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { $ } from 'bun';
|
|
3
|
+
import { decode } from '../lib/run.js';
|
|
4
|
+
import { escapeRegex, getWorkspacePatterns, listReleasePackages } from '../lib/workspace.js';
|
|
3
5
|
import { validateCommitMessage } from './commit-msg.js';
|
|
4
6
|
import { applyWorkspaceGitConfig } from './git-config.js';
|
|
5
7
|
import { syncBunLockfileVersions } from './lockfile.js';
|
|
@@ -16,9 +18,13 @@ export async function initMonorepo(root, options) {
|
|
|
16
18
|
await runInitPacks({ root, syncRuntime: process.env.DEVENV_ROOT !== undefined || options.syncRuntime === true });
|
|
17
19
|
}
|
|
18
20
|
export async function validateMonorepo(root, options = {}) {
|
|
21
|
+
if (options.onlyIfNewWorkspacePackage && !(await hasNewWorkspacePackage(root))) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
19
24
|
const failures = await runValidatePacks({ root, syncRuntime: false }, options);
|
|
20
25
|
if (failures > 0) {
|
|
21
|
-
|
|
26
|
+
const noun = failures === 1 ? 'problem' : 'problems';
|
|
27
|
+
throw new Error(`\n== summary ==\n❌ Monorepo validation failed with ${failures} ${noun}.`);
|
|
22
28
|
}
|
|
23
29
|
console.log('\n== summary ==');
|
|
24
30
|
console.log('Monorepo configuration is valid.');
|
|
@@ -46,10 +52,17 @@ export function validateCommitMessageFile(path) {
|
|
|
46
52
|
throw new Error(error);
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
|
-
export function
|
|
50
|
-
|
|
55
|
+
export function listReleasePackagesForNx(root, options = {}) {
|
|
56
|
+
const packages = listReleasePackages(root)
|
|
51
57
|
.map((pkg) => pkg.name)
|
|
52
58
|
.join(',');
|
|
59
|
+
if (!packages && options.failEmpty) {
|
|
60
|
+
throw new Error('No owned release packages found.');
|
|
61
|
+
}
|
|
62
|
+
if (options.githubOutput) {
|
|
63
|
+
appendFileSync(options.githubOutput, `projects=${packages}\n`);
|
|
64
|
+
}
|
|
65
|
+
return packages;
|
|
53
66
|
}
|
|
54
67
|
export function validatePublicPackageTags(root) {
|
|
55
68
|
if (validatePublicTags(root) > 0) {
|
|
@@ -57,3 +70,23 @@ export function validatePublicPackageTags(root) {
|
|
|
57
70
|
}
|
|
58
71
|
}
|
|
59
72
|
export { applyWorkspaceGitConfig, syncBunLockfileVersions };
|
|
73
|
+
async function hasNewWorkspacePackage(root) {
|
|
74
|
+
const result = await $ `git diff --cached --name-only --diff-filter=A -- ${'*/package.json'}`
|
|
75
|
+
.cwd(root)
|
|
76
|
+
.quiet()
|
|
77
|
+
.nothrow();
|
|
78
|
+
if (result.exitCode !== 0) {
|
|
79
|
+
throw new Error('Unable to inspect staged package manifests.');
|
|
80
|
+
}
|
|
81
|
+
const manifests = decode(result.stdout)
|
|
82
|
+
.split('\n')
|
|
83
|
+
.map((path) => path.trim())
|
|
84
|
+
.filter(Boolean);
|
|
85
|
+
if (manifests.length === 0) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const patterns = getWorkspacePatterns(root)
|
|
89
|
+
.filter((pattern) => pattern.endsWith('/*'))
|
|
90
|
+
.map((pattern) => new RegExp(`^${escapeRegex(pattern.slice(0, -2))}/[^/]+/package\\.json$`));
|
|
91
|
+
return manifests.some((manifest) => patterns.some((pattern) => pattern.test(manifest)));
|
|
92
|
+
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface SyncBunLockfileVersionsOptions {
|
|
2
|
+
log?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export declare function syncBunLockfileVersions(root: string, options?: SyncBunLockfileVersionsOptions): number;
|
|
5
|
+
export declare function validateBunLockfileVersions(root: string): number;
|
|
2
6
|
//# sourceMappingURL=lockfile.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../../src/monorepo/lockfile.ts"],"names":[],"mappings":"AAIA,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../../src/monorepo/lockfile.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,8BAA8B;IAC7C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,8BAAmC,GAAG,MAAM,CA0C1G;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6BhE"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { escapeRegex, getWorkspacePackages } from '../lib/workspace.js';
|
|
4
|
-
export function syncBunLockfileVersions(root) {
|
|
4
|
+
export function syncBunLockfileVersions(root, options = {}) {
|
|
5
|
+
const log = options.log ?? true;
|
|
5
6
|
const lockfilePath = join(root, 'bun.lock');
|
|
6
7
|
if (!existsSync(lockfilePath)) {
|
|
7
8
|
throw new Error('bun.lock not found');
|
|
@@ -15,20 +16,59 @@ export function syncBunLockfileVersions(root) {
|
|
|
15
16
|
const pattern = new RegExp(`("${escaped}":\\s*\\{[^}]*"version":\\s*")([^"]+)(")`);
|
|
16
17
|
const match = lockfile.match(pattern);
|
|
17
18
|
if (!match) {
|
|
18
|
-
|
|
19
|
+
if (log) {
|
|
20
|
+
console.log(`skip: ${relativePath} (not found in lockfile)`);
|
|
21
|
+
}
|
|
19
22
|
continue;
|
|
20
23
|
}
|
|
21
24
|
const lockVersion = match[2];
|
|
22
25
|
if (lockVersion === pkg.version) {
|
|
23
|
-
|
|
26
|
+
if (log) {
|
|
27
|
+
console.log(`ok: ${relativePath} = ${pkg.version}`);
|
|
28
|
+
}
|
|
24
29
|
continue;
|
|
25
30
|
}
|
|
26
31
|
lockfile = lockfile.replace(pattern, `$1${pkg.version}$3`);
|
|
27
|
-
|
|
32
|
+
if (log) {
|
|
33
|
+
console.log(`fix: ${relativePath}: ${lockVersion} -> ${pkg.version}`);
|
|
34
|
+
}
|
|
28
35
|
updated++;
|
|
29
36
|
}
|
|
30
37
|
if (updated > 0) {
|
|
31
38
|
writeFileSync(lockfilePath, lockfile);
|
|
32
39
|
}
|
|
33
|
-
|
|
40
|
+
if (log) {
|
|
41
|
+
console.log(updated > 0 ? `Updated ${updated} workspace version(s) in bun.lock` : 'All workspace versions already in sync.');
|
|
42
|
+
}
|
|
43
|
+
return updated;
|
|
44
|
+
}
|
|
45
|
+
export function validateBunLockfileVersions(root) {
|
|
46
|
+
const lockfilePath = join(root, 'bun.lock');
|
|
47
|
+
if (!existsSync(lockfilePath)) {
|
|
48
|
+
console.error('bun.lock not found');
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
const packages = getWorkspacePackages(root);
|
|
52
|
+
const lockfile = readFileSync(lockfilePath, 'utf8');
|
|
53
|
+
let failures = 0;
|
|
54
|
+
for (const pkg of packages) {
|
|
55
|
+
const relativePath = pkg.path.replaceAll('\\', '/');
|
|
56
|
+
const escaped = escapeRegex(relativePath);
|
|
57
|
+
const pattern = new RegExp(`("${escaped}":\\s*\\{[^}]*"version":\\s*")([^"]+)(")`);
|
|
58
|
+
const match = lockfile.match(pattern);
|
|
59
|
+
if (!match) {
|
|
60
|
+
console.error(`bun.lock missing workspace entry for ${relativePath}`);
|
|
61
|
+
failures++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const lockVersion = match[2];
|
|
65
|
+
if (lockVersion !== pkg.version) {
|
|
66
|
+
console.error(`${relativePath}: bun.lock workspace version must be ${pkg.version}, got ${lockVersion}`);
|
|
67
|
+
failures++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (failures === 0) {
|
|
71
|
+
console.log('bun.lock workspace versions are valid.');
|
|
72
|
+
}
|
|
73
|
+
return failures;
|
|
34
74
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface FileResult {
|
|
2
2
|
target: string;
|
|
3
|
-
action: 'created' | 'updated' | 'unchanged' | 'skipped-symlink' | 'drifted' | 'ok-symlink';
|
|
3
|
+
action: 'created' | 'updated' | 'unchanged' | 'skipped' | 'skipped-symlink' | 'drifted' | 'ok-symlink';
|
|
4
4
|
}
|
|
5
5
|
export declare function applyManagedFiles(root: string, mode: 'update' | 'check' | 'diff'): FileResult[];
|
|
6
6
|
export declare function printResults(results: FileResult[]): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"managed-files.d.ts","sourceRoot":"","sources":["../../src/monorepo/managed-files.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,YAAY,CAAC;CACxG;AAwED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,EAAE,CAG/F;AA0ED,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAIxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { readPackageJson } from '../lib/workspace.js';
|
|
4
|
+
import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
|
|
5
|
+
import { renderPublishWorkflowYaml } from './publish-workflow.js';
|
|
5
6
|
const managedFiles = [
|
|
6
7
|
{
|
|
7
8
|
kind: 'raw',
|
|
@@ -32,9 +33,10 @@ const managedFiles = [
|
|
|
32
33
|
target: '.github/workflows/ci.yml',
|
|
33
34
|
},
|
|
34
35
|
{
|
|
35
|
-
kind: '
|
|
36
|
-
source: '
|
|
36
|
+
kind: 'generated',
|
|
37
|
+
source: 'publish-workflow',
|
|
37
38
|
target: '.github/workflows/publish.yml',
|
|
39
|
+
releasePackagesOnly: true,
|
|
38
40
|
},
|
|
39
41
|
{
|
|
40
42
|
kind: 'template',
|
|
@@ -64,11 +66,15 @@ const managedFiles = [
|
|
|
64
66
|
];
|
|
65
67
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
66
68
|
export function applyManagedFiles(root, mode) {
|
|
67
|
-
|
|
69
|
+
const context = getManagedFileContext(root);
|
|
70
|
+
return managedFiles.map((file) => applyManagedFile(root, file, mode, context));
|
|
68
71
|
}
|
|
69
|
-
function applyManagedFile(root, file, mode) {
|
|
72
|
+
function applyManagedFile(root, file, mode, context) {
|
|
73
|
+
if (file.releasePackagesOnly === true && !context.hasReleasePackages) {
|
|
74
|
+
return { target: file.target, action: 'skipped' };
|
|
75
|
+
}
|
|
70
76
|
const target = resolve(root, file.target);
|
|
71
|
-
const content = getManagedContent(
|
|
77
|
+
const content = getManagedContent(file, context);
|
|
72
78
|
if (existsSync(target)) {
|
|
73
79
|
const info = lstatSync(target);
|
|
74
80
|
if (info.isSymbolicLink()) {
|
|
@@ -93,22 +99,33 @@ function applyManagedFile(root, file, mode) {
|
|
|
93
99
|
writeManagedFile(target, content, file.executable === true);
|
|
94
100
|
return { target: file.target, action: 'created' };
|
|
95
101
|
}
|
|
96
|
-
function getManagedContent(
|
|
102
|
+
function getManagedContent(file, context) {
|
|
103
|
+
if (file.kind === 'generated') {
|
|
104
|
+
if (file.source === 'publish-workflow') {
|
|
105
|
+
return renderPublishWorkflowYaml({ repoName: context.repoName });
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`Unknown generated managed file source ${file.source}`);
|
|
108
|
+
}
|
|
97
109
|
const sourceRoot = file.kind === 'raw' ? 'managed/raw' : 'managed/templates';
|
|
98
110
|
const sourcePath = join(packageRoot, sourceRoot, file.source);
|
|
99
111
|
const content = readFileSync(sourcePath, 'utf8');
|
|
100
112
|
if (file.kind === 'raw') {
|
|
101
113
|
return content;
|
|
102
114
|
}
|
|
103
|
-
return renderTemplate(
|
|
115
|
+
return renderTemplate(context, content);
|
|
104
116
|
}
|
|
105
|
-
function
|
|
117
|
+
function getManagedFileContext(root) {
|
|
106
118
|
const packageJson = readPackageJson(join(root, 'package.json'));
|
|
107
119
|
const repoName = packageJson?.name ?? 'monorepo';
|
|
108
120
|
const nodeModulesCacheKey = existsSync(join(root, 'bun.lock'))
|
|
109
121
|
? `$${"{{ hashFiles('bun.lock', 'package.json', 'packages/*/package.json') }}"}`
|
|
110
122
|
: `$${"{{ hashFiles('bun.lockb', 'package.json', 'packages/*/package.json') }}"}`;
|
|
111
|
-
return
|
|
123
|
+
return { hasReleasePackages: listReleasePackages(root, packageJson).length > 0, nodeModulesCacheKey, repoName };
|
|
124
|
+
}
|
|
125
|
+
function renderTemplate(context, template) {
|
|
126
|
+
return template
|
|
127
|
+
.replaceAll('{{REPO_NAME}}', context.repoName)
|
|
128
|
+
.replaceAll('{{NODE_MODULES_CACHE_KEY}}', context.nodeModulesCacheKey);
|
|
112
129
|
}
|
|
113
130
|
function writeManagedFile(path, content, executable) {
|
|
114
131
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -3,7 +3,7 @@ export async function fixPackageHygiene(root) {
|
|
|
3
3
|
await run('sherif', ['--fix', '--select', 'highest'], root);
|
|
4
4
|
}
|
|
5
5
|
export async function validatePackageHygiene(root) {
|
|
6
|
-
const status = await runStatus('sherif', [], root);
|
|
6
|
+
const status = await runStatus('sherif', ['--fail-on-warnings'], root);
|
|
7
7
|
if (status !== 0) {
|
|
8
8
|
console.error('sherif package hygiene validation failed');
|
|
9
9
|
return 1;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
export declare const SMOO_NX_VERSION_ACTIONS = "@smoothbricks/cli/nx-version-actions";
|
|
2
|
+
export declare const SMOO_NX_RELEASE_TAG_PATTERN = "{projectName}@{version}";
|
|
1
3
|
export declare function applyPublicPackageDefaults(root: string): void;
|
|
2
4
|
export declare function applyWorkspaceDependencyDefaults(root: string): void;
|
|
5
|
+
export declare function applyNxReleaseDefaults(root: string): void;
|
|
3
6
|
export declare function validateRootPackagePolicy(root: string): number;
|
|
4
7
|
export declare function validateNxReleaseConfig(root: string): number;
|
|
5
8
|
export declare function validatePublicTags(root: string): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-policy.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"package-policy.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-policy.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,uBAAuB,yCAAyC,CAAC;AAC9E,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AAErE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAuC7D;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAWnE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAqCzD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoC9D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkF5D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0DlE;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBlE"}
|