@smoothbricks/cli 0.3.1 → 0.3.3
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/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +40 -6
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +6 -0
- package/dist/monorepo/packed-manifest.d.ts.map +1 -1
- package/dist/monorepo/packed-manifest.js +36 -3
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +1 -5
- package/managed/raw/tooling/direnv/repo-path +34 -0
- package/managed/raw/tooling/git-hooks/commit-msg.sh +1 -7
- package/managed/raw/tooling/git-hooks/pre-commit.sh +1 -7
- package/managed/templates/github/actions/save-nix-devenv/action.yml +3 -3
- package/package.json +2 -2
- package/src/monorepo/lockfile.ts +41 -6
- package/src/monorepo/managed-files.ts +6 -0
- package/src/monorepo/packed-manifest.ts +36 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../../src/monorepo/lockfile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lockfile.d.ts","sourceRoot":"","sources":["../../src/monorepo/lockfile.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,8BAA8B;IAC7C,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAWD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,8BAAmC,GAAG,MAAM,CAkD1G;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiChE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
1
2
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { escapeRegex, getWorkspacePackages } from '../lib/workspace.js';
|
|
@@ -20,6 +21,12 @@ export function syncBunLockfileVersions(root, options = {}) {
|
|
|
20
21
|
let lockfile = readFileSync(lockfilePath, 'utf8');
|
|
21
22
|
let updated = 0;
|
|
22
23
|
for (const pkg of packages) {
|
|
24
|
+
// If the package.json version is a prerelease (e.g. 0.2.2-next.0) it may
|
|
25
|
+
// never have been published. Use the latest stable git tag version so
|
|
26
|
+
// that `bun pm pack` writes an installable dependency range for consumers.
|
|
27
|
+
const targetVersion = pkg.version.includes('-')
|
|
28
|
+
? (latestStableTagVersion(root, pkg.projectName) ?? pkg.version)
|
|
29
|
+
: pkg.version;
|
|
23
30
|
const relativePath = pkg.path.replaceAll('\\', '/');
|
|
24
31
|
const escaped = escapeRegex(relativePath);
|
|
25
32
|
const pattern = new RegExp(`("${escaped}":\\s*\\{[^}]*"version":\\s*")([^"]+)(")`);
|
|
@@ -31,15 +38,16 @@ export function syncBunLockfileVersions(root, options = {}) {
|
|
|
31
38
|
continue;
|
|
32
39
|
}
|
|
33
40
|
const lockVersion = match[2];
|
|
34
|
-
if (lockVersion ===
|
|
41
|
+
if (lockVersion === targetVersion) {
|
|
35
42
|
if (log) {
|
|
36
|
-
console.log(`ok: ${relativePath} = ${
|
|
43
|
+
console.log(`ok: ${relativePath} = ${targetVersion}`);
|
|
37
44
|
}
|
|
38
45
|
continue;
|
|
39
46
|
}
|
|
40
|
-
lockfile = lockfile.replace(pattern, `$1${
|
|
47
|
+
lockfile = lockfile.replace(pattern, `$1${targetVersion}$3`);
|
|
41
48
|
if (log) {
|
|
42
|
-
|
|
49
|
+
const suffix = targetVersion !== pkg.version ? ` (latest stable tag; package.json has ${pkg.version})` : '';
|
|
50
|
+
console.log(`fix: ${relativePath}: ${lockVersion} -> ${targetVersion}${suffix}`);
|
|
43
51
|
}
|
|
44
52
|
updated++;
|
|
45
53
|
}
|
|
@@ -61,6 +69,9 @@ export function validateBunLockfileVersions(root) {
|
|
|
61
69
|
const lockfile = readFileSync(lockfilePath, 'utf8');
|
|
62
70
|
let failures = 0;
|
|
63
71
|
for (const pkg of packages) {
|
|
72
|
+
const targetVersion = pkg.version.includes('-')
|
|
73
|
+
? (latestStableTagVersion(root, pkg.projectName) ?? pkg.version)
|
|
74
|
+
: pkg.version;
|
|
64
75
|
const relativePath = pkg.path.replaceAll('\\', '/');
|
|
65
76
|
const escaped = escapeRegex(relativePath);
|
|
66
77
|
const pattern = new RegExp(`("${escaped}":\\s*\\{[^}]*"version":\\s*")([^"]+)(")`);
|
|
@@ -71,8 +82,8 @@ export function validateBunLockfileVersions(root) {
|
|
|
71
82
|
continue;
|
|
72
83
|
}
|
|
73
84
|
const lockVersion = match[2];
|
|
74
|
-
if (lockVersion !==
|
|
75
|
-
console.error(`${relativePath}: bun.lock workspace version must be ${
|
|
85
|
+
if (lockVersion !== targetVersion) {
|
|
86
|
+
console.error(`${relativePath}: bun.lock workspace version must be ${targetVersion}, got ${lockVersion}`);
|
|
76
87
|
failures++;
|
|
77
88
|
}
|
|
78
89
|
}
|
|
@@ -81,3 +92,26 @@ export function validateBunLockfileVersions(root) {
|
|
|
81
92
|
}
|
|
82
93
|
return failures;
|
|
83
94
|
}
|
|
95
|
+
function latestStableTagVersion(root, projectName) {
|
|
96
|
+
try {
|
|
97
|
+
const output = execSync(`git tag --list '${projectName}@*' --sort=-v:refname`, {
|
|
98
|
+
cwd: root,
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
101
|
+
});
|
|
102
|
+
const prefix = `${projectName}@`;
|
|
103
|
+
for (const line of output.split('\n')) {
|
|
104
|
+
const tag = line.trim();
|
|
105
|
+
if (!tag.startsWith(prefix))
|
|
106
|
+
continue;
|
|
107
|
+
const version = tag.slice(prefix.length);
|
|
108
|
+
if (version && !version.includes('-')) {
|
|
109
|
+
return version;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AA8ED,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"}
|
|
@@ -4,6 +4,12 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
|
|
5
5
|
import { renderPublishWorkflowYaml } from './publish-workflow.js';
|
|
6
6
|
const managedFiles = [
|
|
7
|
+
{
|
|
8
|
+
kind: 'raw',
|
|
9
|
+
source: 'tooling/direnv/repo-path',
|
|
10
|
+
target: 'tooling/direnv/repo-path',
|
|
11
|
+
executable: true,
|
|
12
|
+
},
|
|
7
13
|
{
|
|
8
14
|
kind: 'raw',
|
|
9
15
|
source: 'tooling/direnv/github-actions-bootstrap.sh',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packed-manifest.d.ts","sourceRoot":"","sources":["../../src/monorepo/packed-manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"packed-manifest.d.ts","sourceRoot":"","sources":["../../src/monorepo/packed-manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAUlC;AAED,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,WAAW,EAC1B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,MAAM,EAAE,CA8CV"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
1
2
|
import { $ } from 'bun';
|
|
2
3
|
import { isRecord, recordProperty } from '../lib/json.js';
|
|
3
4
|
import { decode } from '../lib/run.js';
|
|
@@ -14,7 +15,9 @@ export async function readPackedPackageJson(root, tarball, packageName) {
|
|
|
14
15
|
return parsed;
|
|
15
16
|
}
|
|
16
17
|
export function validatePackedWorkspaceDependencies(root, sourcePackage, packedPackage) {
|
|
17
|
-
const
|
|
18
|
+
const workspacePackages = getWorkspacePackages(root);
|
|
19
|
+
const workspaceVersions = new Map(workspacePackages.map((pkg) => [pkg.name, pkg.version]));
|
|
20
|
+
const projectNameByPackage = new Map(workspacePackages.map((pkg) => [pkg.name, pkg.projectName]));
|
|
18
21
|
const failures = [];
|
|
19
22
|
for (const field of workspaceDependencyFields) {
|
|
20
23
|
const sourceDependencies = recordProperty(sourcePackage.json, field);
|
|
@@ -39,8 +42,15 @@ export function validatePackedWorkspaceDependencies(root, sourcePackage, packedP
|
|
|
39
42
|
if (sourceRange !== 'workspace:*') {
|
|
40
43
|
failures.push(`${sourcePackage.path}: source ${field}.${name} must use workspace:*`);
|
|
41
44
|
}
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
// When the workspace dep is at a prerelease version that was never
|
|
46
|
+
// published, the lockfile sync rewrites it to the latest stable tag
|
|
47
|
+
// version. Accept that version as valid.
|
|
48
|
+
const projectName = projectNameByPackage.get(name);
|
|
49
|
+
const expectedVersion = workspaceVersion.includes('-') && projectName
|
|
50
|
+
? (latestStableTagVersion(root, projectName) ?? workspaceVersion)
|
|
51
|
+
: workspaceVersion;
|
|
52
|
+
if (packedRange !== expectedVersion) {
|
|
53
|
+
failures.push(`${sourcePackage.path}: packed ${field}.${name} must be ${expectedVersion}, got ${formatRange(packedRange)}`);
|
|
44
54
|
}
|
|
45
55
|
}
|
|
46
56
|
}
|
|
@@ -49,3 +59,26 @@ export function validatePackedWorkspaceDependencies(root, sourcePackage, packedP
|
|
|
49
59
|
function formatRange(value) {
|
|
50
60
|
return typeof value === 'string' ? value : '<missing>';
|
|
51
61
|
}
|
|
62
|
+
function latestStableTagVersion(root, projectName) {
|
|
63
|
+
try {
|
|
64
|
+
const output = execSync(`git tag --list '${projectName}@*' --sort=-v:refname`, {
|
|
65
|
+
cwd: root,
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
68
|
+
});
|
|
69
|
+
const prefix = `${projectName}@`;
|
|
70
|
+
for (const line of output.split('\n')) {
|
|
71
|
+
const tag = line.trim();
|
|
72
|
+
if (!tag.startsWith(prefix))
|
|
73
|
+
continue;
|
|
74
|
+
const version = tag.slice(prefix.length);
|
|
75
|
+
if (version && !version.includes('-')) {
|
|
76
|
+
return version;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -14,11 +14,7 @@ clear_devenv_cache_state() {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
add_repo_paths() {
|
|
17
|
-
|
|
18
|
-
echo "$repo_root/tooling/direnv/.devenv/profile/bin"
|
|
19
|
-
echo "$repo_root/node_modules/.bin"
|
|
20
|
-
echo "$repo_root/tooling"
|
|
21
|
-
} >> "$GITHUB_PATH"
|
|
17
|
+
"$repo_root/tooling/direnv/repo-path" --github-path
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
restore_nix_store() {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Single source of truth for repo-local PATH entries.
|
|
3
|
+
# All tooling that sets up PATH must call this instead of hardcoding paths.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# export PATH="$(tooling/direnv/repo-path)" # shell
|
|
7
|
+
# tooling/direnv/repo-path --github-path # CI — appends to $GITHUB_PATH
|
|
8
|
+
# tooling/direnv/repo-path --print # debug — one entry per line
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
12
|
+
|
|
13
|
+
# Order: most-specific → least-specific.
|
|
14
|
+
entries=(
|
|
15
|
+
"$root/tooling/direnv/.devenv/profile/bin"
|
|
16
|
+
"$root/tooling/git-hooks"
|
|
17
|
+
"$root/tooling"
|
|
18
|
+
"$root/tooling/node_modules/.bin"
|
|
19
|
+
"$root/node_modules/.bin"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
case "${1:-}" in
|
|
23
|
+
--github-path)
|
|
24
|
+
# GITHUB_PATH prepends entries; last line written = first in PATH.
|
|
25
|
+
# Reverse so most-specific directories are searched first.
|
|
26
|
+
for (( i=${#entries[@]}-1; i>=0; i-- )); do
|
|
27
|
+
echo "${entries[i]}"
|
|
28
|
+
done >> "$GITHUB_PATH" ;;
|
|
29
|
+
--print)
|
|
30
|
+
printf '%s\n' "${entries[@]}" ;;
|
|
31
|
+
*)
|
|
32
|
+
IFS=:
|
|
33
|
+
echo "${entries[*]}:$PATH" ;;
|
|
34
|
+
esac
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
cd "$(git rev-parse --show-toplevel)"
|
|
3
3
|
TOOLING="$PWD/tooling"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
# 1. git-hooks/ – hook-specific helper scripts
|
|
7
|
-
# 2. tooling/ – the repo toolbox (curated tools like the smoo wrapper)
|
|
8
|
-
# 3. tooling/node_modules/.bin – toolbox installed deps (@smoothbricks/cli)
|
|
9
|
-
# 4. node_modules/.bin – root workspace deps (Nx, biome, etc.)
|
|
10
|
-
# 5. devenv profile – upstream native/system tools
|
|
11
|
-
export PATH="$TOOLING/git-hooks:$TOOLING:$TOOLING/node_modules/.bin:$PWD/node_modules/.bin:$TOOLING/direnv/.devenv/profile/bin:$PATH"
|
|
5
|
+
export PATH="$("$TOOLING/direnv/repo-path")"
|
|
12
6
|
|
|
13
7
|
set -e -o pipefail
|
|
14
8
|
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
cd "$(git rev-parse --show-toplevel)"
|
|
3
3
|
TOOLING="$PWD/tooling"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
# 1. git-hooks/ – hook-specific helper scripts
|
|
7
|
-
# 2. tooling/ – the repo toolbox (curated tools like the smoo wrapper)
|
|
8
|
-
# 3. tooling/node_modules/.bin – toolbox installed deps (@smoothbricks/cli)
|
|
9
|
-
# 4. node_modules/.bin – root workspace deps (Nx, biome, etc.)
|
|
10
|
-
# 5. devenv profile – upstream native/system tools
|
|
11
|
-
export PATH="$TOOLING/git-hooks:$TOOLING:$TOOLING/node_modules/.bin:$PWD/node_modules/.bin:$TOOLING/direnv/.devenv/profile/bin:$PATH"
|
|
5
|
+
export PATH="$("$TOOLING/direnv/repo-path")"
|
|
12
6
|
|
|
13
7
|
set -e -o pipefail
|
|
14
8
|
|
|
@@ -20,9 +20,9 @@ runs:
|
|
|
20
20
|
working-directory: tooling/direnv
|
|
21
21
|
# build-shell normally adds repo paths to GITHUB_PATH, but this cleanup
|
|
22
22
|
# must still run when build-shell fails before that happens.
|
|
23
|
-
run:
|
|
24
|
-
PATH="$
|
|
25
|
-
github-ci cleanup-cache
|
|
23
|
+
run: |
|
|
24
|
+
export PATH="$(../../tooling/direnv/repo-path)"
|
|
25
|
+
smoo github-ci cleanup-cache
|
|
26
26
|
|
|
27
27
|
- name: 🧊 Save Nix profiles and store NAR
|
|
28
28
|
if: ${{ steps.cleanup-cache.outputs.cache-ready == 'true' && inputs.nix-cache-hit != 'true' }}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smoothbricks/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SmoothBricks monorepo automation CLI",
|
|
6
6
|
"bin": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
48
|
-
"@smoothbricks/nx-plugin": "0.2.
|
|
48
|
+
"@smoothbricks/nx-plugin": "0.2.2",
|
|
49
49
|
"@smoothbricks/validation": "0.1.1",
|
|
50
50
|
"commander": "^14.0.3",
|
|
51
51
|
"publint": "^0.3.18",
|
package/src/monorepo/lockfile.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
1
2
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { escapeRegex, getWorkspacePackages } from '../lib/workspace.js';
|
|
@@ -25,6 +26,13 @@ export function syncBunLockfileVersions(root: string, options: SyncBunLockfileVe
|
|
|
25
26
|
let lockfile = readFileSync(lockfilePath, 'utf8');
|
|
26
27
|
let updated = 0;
|
|
27
28
|
for (const pkg of packages) {
|
|
29
|
+
// If the package.json version is a prerelease (e.g. 0.2.2-next.0) it may
|
|
30
|
+
// never have been published. Use the latest stable git tag version so
|
|
31
|
+
// that `bun pm pack` writes an installable dependency range for consumers.
|
|
32
|
+
const targetVersion = pkg.version.includes('-')
|
|
33
|
+
? (latestStableTagVersion(root, pkg.projectName) ?? pkg.version)
|
|
34
|
+
: pkg.version;
|
|
35
|
+
|
|
28
36
|
const relativePath = pkg.path.replaceAll('\\', '/');
|
|
29
37
|
const escaped = escapeRegex(relativePath);
|
|
30
38
|
const pattern = new RegExp(`("${escaped}":\\s*\\{[^}]*"version":\\s*")([^"]+)(")`);
|
|
@@ -36,15 +44,16 @@ export function syncBunLockfileVersions(root: string, options: SyncBunLockfileVe
|
|
|
36
44
|
continue;
|
|
37
45
|
}
|
|
38
46
|
const lockVersion = match[2];
|
|
39
|
-
if (lockVersion ===
|
|
47
|
+
if (lockVersion === targetVersion) {
|
|
40
48
|
if (log) {
|
|
41
|
-
console.log(`ok: ${relativePath} = ${
|
|
49
|
+
console.log(`ok: ${relativePath} = ${targetVersion}`);
|
|
42
50
|
}
|
|
43
51
|
continue;
|
|
44
52
|
}
|
|
45
|
-
lockfile = lockfile.replace(pattern, `$1${
|
|
53
|
+
lockfile = lockfile.replace(pattern, `$1${targetVersion}$3`);
|
|
46
54
|
if (log) {
|
|
47
|
-
|
|
55
|
+
const suffix = targetVersion !== pkg.version ? ` (latest stable tag; package.json has ${pkg.version})` : '';
|
|
56
|
+
console.log(`fix: ${relativePath}: ${lockVersion} -> ${targetVersion}${suffix}`);
|
|
48
57
|
}
|
|
49
58
|
updated++;
|
|
50
59
|
}
|
|
@@ -69,6 +78,10 @@ export function validateBunLockfileVersions(root: string): number {
|
|
|
69
78
|
const lockfile = readFileSync(lockfilePath, 'utf8');
|
|
70
79
|
let failures = 0;
|
|
71
80
|
for (const pkg of packages) {
|
|
81
|
+
const targetVersion = pkg.version.includes('-')
|
|
82
|
+
? (latestStableTagVersion(root, pkg.projectName) ?? pkg.version)
|
|
83
|
+
: pkg.version;
|
|
84
|
+
|
|
72
85
|
const relativePath = pkg.path.replaceAll('\\', '/');
|
|
73
86
|
const escaped = escapeRegex(relativePath);
|
|
74
87
|
const pattern = new RegExp(`("${escaped}":\\s*\\{[^}]*"version":\\s*")([^"]+)(")`);
|
|
@@ -79,8 +92,8 @@ export function validateBunLockfileVersions(root: string): number {
|
|
|
79
92
|
continue;
|
|
80
93
|
}
|
|
81
94
|
const lockVersion = match[2];
|
|
82
|
-
if (lockVersion !==
|
|
83
|
-
console.error(`${relativePath}: bun.lock workspace version must be ${
|
|
95
|
+
if (lockVersion !== targetVersion) {
|
|
96
|
+
console.error(`${relativePath}: bun.lock workspace version must be ${targetVersion}, got ${lockVersion}`);
|
|
84
97
|
failures++;
|
|
85
98
|
}
|
|
86
99
|
}
|
|
@@ -89,3 +102,25 @@ export function validateBunLockfileVersions(root: string): number {
|
|
|
89
102
|
}
|
|
90
103
|
return failures;
|
|
91
104
|
}
|
|
105
|
+
|
|
106
|
+
function latestStableTagVersion(root: string, projectName: string): string | null {
|
|
107
|
+
try {
|
|
108
|
+
const output = execSync(`git tag --list '${projectName}@*' --sort=-v:refname`, {
|
|
109
|
+
cwd: root,
|
|
110
|
+
encoding: 'utf8',
|
|
111
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
112
|
+
});
|
|
113
|
+
const prefix = `${projectName}@`;
|
|
114
|
+
for (const line of output.split('\n')) {
|
|
115
|
+
const tag = line.trim();
|
|
116
|
+
if (!tag.startsWith(prefix)) continue;
|
|
117
|
+
const version = tag.slice(prefix.length);
|
|
118
|
+
if (version && !version.includes('-')) {
|
|
119
|
+
return version;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -26,6 +26,12 @@ interface ManagedFileContext {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const managedFiles: ManagedFile[] = [
|
|
29
|
+
{
|
|
30
|
+
kind: 'raw',
|
|
31
|
+
source: 'tooling/direnv/repo-path',
|
|
32
|
+
target: 'tooling/direnv/repo-path',
|
|
33
|
+
executable: true,
|
|
34
|
+
},
|
|
29
35
|
{
|
|
30
36
|
kind: 'raw',
|
|
31
37
|
source: 'tooling/direnv/github-actions-bootstrap.sh',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
1
2
|
import { $ } from 'bun';
|
|
2
3
|
import { isRecord, recordProperty } from '../lib/json.js';
|
|
3
4
|
import { decode } from '../lib/run.js';
|
|
@@ -25,7 +26,9 @@ export function validatePackedWorkspaceDependencies(
|
|
|
25
26
|
sourcePackage: PackageInfo,
|
|
26
27
|
packedPackage: Record<string, unknown>,
|
|
27
28
|
): string[] {
|
|
28
|
-
const
|
|
29
|
+
const workspacePackages = getWorkspacePackages(root);
|
|
30
|
+
const workspaceVersions = new Map(workspacePackages.map((pkg) => [pkg.name, pkg.version]));
|
|
31
|
+
const projectNameByPackage = new Map(workspacePackages.map((pkg) => [pkg.name, pkg.projectName]));
|
|
29
32
|
const failures: string[] = [];
|
|
30
33
|
for (const field of workspaceDependencyFields) {
|
|
31
34
|
const sourceDependencies = recordProperty(sourcePackage.json, field);
|
|
@@ -52,9 +55,17 @@ export function validatePackedWorkspaceDependencies(
|
|
|
52
55
|
if (sourceRange !== 'workspace:*') {
|
|
53
56
|
failures.push(`${sourcePackage.path}: source ${field}.${name} must use workspace:*`);
|
|
54
57
|
}
|
|
55
|
-
|
|
58
|
+
// When the workspace dep is at a prerelease version that was never
|
|
59
|
+
// published, the lockfile sync rewrites it to the latest stable tag
|
|
60
|
+
// version. Accept that version as valid.
|
|
61
|
+
const projectName = projectNameByPackage.get(name);
|
|
62
|
+
const expectedVersion =
|
|
63
|
+
workspaceVersion.includes('-') && projectName
|
|
64
|
+
? (latestStableTagVersion(root, projectName) ?? workspaceVersion)
|
|
65
|
+
: workspaceVersion;
|
|
66
|
+
if (packedRange !== expectedVersion) {
|
|
56
67
|
failures.push(
|
|
57
|
-
`${sourcePackage.path}: packed ${field}.${name} must be ${
|
|
68
|
+
`${sourcePackage.path}: packed ${field}.${name} must be ${expectedVersion}, got ${formatRange(packedRange)}`,
|
|
58
69
|
);
|
|
59
70
|
}
|
|
60
71
|
}
|
|
@@ -65,3 +76,25 @@ export function validatePackedWorkspaceDependencies(
|
|
|
65
76
|
function formatRange(value: unknown): string {
|
|
66
77
|
return typeof value === 'string' ? value : '<missing>';
|
|
67
78
|
}
|
|
79
|
+
|
|
80
|
+
function latestStableTagVersion(root: string, projectName: string): string | null {
|
|
81
|
+
try {
|
|
82
|
+
const output = execSync(`git tag --list '${projectName}@*' --sort=-v:refname`, {
|
|
83
|
+
cwd: root,
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
|
+
});
|
|
87
|
+
const prefix = `${projectName}@`;
|
|
88
|
+
for (const line of output.split('\n')) {
|
|
89
|
+
const tag = line.trim();
|
|
90
|
+
if (!tag.startsWith(prefix)) continue;
|
|
91
|
+
const version = tag.slice(prefix.length);
|
|
92
|
+
if (version && !version.includes('-')) {
|
|
93
|
+
return version;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|