@shazhou/proman-core 0.9.0 → 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.1 — 2026-06-13
4
+
5
+ - Update release workflow for monorepo and fix smoke test workspace deps
6
+
7
+ **release.yaml v2**: Updated for monorepo structure — uses `proman bump` and
8
+ `proman publish` instead of raw `npm version`/`pnpm publish`. Preflight checks
9
+ all packages' versions on npm. Publisher verifies `workspace:*` was resolved
10
+ correctly after publish.
11
+
12
+ **smoke-test**: `smokeTestTarball` now accepts `workspacePackages` map and
13
+ symlinks workspace dependencies into extracted tarball's `node_modules/`,
14
+ fixing smoke test failure for packages with workspace deps (e.g. CLI → core).
15
+
16
+ **smoke-test parse fix**: Extract `.tgz` filename from pnpm pack verbose output
17
+ via regex instead of treating entire stdout as filename.
18
+
19
+ **docs**: Renamed `lint` → `check` across all documentation and pre-push hook.
20
+
3
21
  ## 0.9.0 — 2026-06-13
4
22
 
5
23
  - Refactor proman into a monorepo with `@shazhou/proman-core` and `@shazhou/proman` packages.
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,iBAAiB,CAAA;AAGxB,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,aAAa,CAAC,EAAE,gBAAgB,CAAA;IAChC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAoBD;;;GAGG;AACH,wBAAsB,OAAO,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuHtE"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,iBAAiB,CAAA;AAGxB,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,aAAa,CAAC,EAAE,gBAAgB,CAAA;IAChC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAoBD;;;GAGG;AACH,wBAAsB,OAAO,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4HtE"}
@@ -75,7 +75,12 @@ export async function publish(opts = {}) {
75
75
  }
76
76
  // Smoke test: validate tarball before publishing
77
77
  try {
78
- await smokeTestTarball(pkgDir, spawn);
78
+ // Build workspace package map for symlink resolution
79
+ const workspacePackages = {};
80
+ for (const pkg of cfg.packages) {
81
+ workspacePackages[pkg.name] = resolve(cwd, pkg.path);
82
+ }
83
+ await smokeTestTarball(pkgDir, spawn, workspacePackages);
79
84
  }
80
85
  catch (err) {
81
86
  const message = err.message;
@@ -1,7 +1,17 @@
1
1
  import type { SpawnFn } from './npm.js';
2
+ /**
3
+ * Map of workspace package names → absolute paths on disk.
4
+ * Used to symlink workspace dependencies into the smoke test directory
5
+ * so bin commands can resolve them without npm install.
6
+ */
7
+ export type WorkspacePackages = Record<string, string>;
2
8
  /**
3
9
  * Smoke test a package tarball by extracting it and running bin commands.
4
10
  * Validates that the packaged artifact actually works before publishing.
11
+ *
12
+ * @param workspacePackages - Map of workspace package names to their absolute
13
+ * paths. When provided, workspace dependencies are symlinked into the
14
+ * extracted tarball's node_modules so bin commands can resolve them.
5
15
  */
6
- export declare function smokeTestTarball(pkgDir: string, spawn: SpawnFn): Promise<void>;
16
+ export declare function smokeTestTarball(pkgDir: string, spawn: SpawnFn, workspacePackages?: WorkspacePackages): Promise<void>;
7
17
  //# sourceMappingURL=smoke-test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"smoke-test.d.ts","sourceRoot":"","sources":["../../src/utils/smoke-test.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAQvC;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DpF"}
1
+ {"version":3,"file":"smoke-test.d.ts","sourceRoot":"","sources":["../../src/utils/smoke-test.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AASvC;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,OAAO,CAAC,IAAI,CAAC,CAqFf"}
@@ -1,11 +1,15 @@
1
- import { readFile, rm } from 'node:fs/promises';
1
+ import { mkdir, readFile, rm, symlink } from 'node:fs/promises';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  /**
5
5
  * Smoke test a package tarball by extracting it and running bin commands.
6
6
  * Validates that the packaged artifact actually works before publishing.
7
+ *
8
+ * @param workspacePackages - Map of workspace package names to their absolute
9
+ * paths. When provided, workspace dependencies are symlinked into the
10
+ * extracted tarball's node_modules so bin commands can resolve them.
7
11
  */
8
- export async function smokeTestTarball(pkgDir, spawn) {
12
+ export async function smokeTestTarball(pkgDir, spawn, workspacePackages) {
9
13
  // Read package.json to check for bin entries
10
14
  const pkgJsonPath = join(pkgDir, 'package.json');
11
15
  const pkgJsonText = await readFile(pkgJsonPath, 'utf8');
@@ -25,10 +29,13 @@ export async function smokeTestTarball(pkgDir, spawn) {
25
29
  if (packResult.code !== 0) {
26
30
  throw new Error(`pnpm pack failed: ${packResult.stderr || packResult.stdout}`);
27
31
  }
28
- const tarballName = packResult.stdout.trim();
29
- if (!tarballName) {
30
- throw new Error('pnpm pack did not return tarball filename');
32
+ // pnpm pack outputs verbose info (📦, file list, etc.)
33
+ // Extract the .tgz filename from the output
34
+ const tgzMatch = packResult.stdout.match(/[\w@.-]+\.tgz/);
35
+ if (!tgzMatch) {
36
+ throw new Error(`pnpm pack did not return tarball filename. Output: ${packResult.stdout}`);
31
37
  }
38
+ const tarballName = tgzMatch[0];
32
39
  // Step 2: Extract tarball to temp directory
33
40
  const { mkdtemp } = await import('node:fs/promises');
34
41
  const testDir = await mkdtemp(join(tmpdir(), 'proman-smoke-'));
@@ -41,7 +48,25 @@ export async function smokeTestTarball(pkgDir, spawn) {
41
48
  }
42
49
  // pnpm pack creates a 'package/' directory inside the tarball
43
50
  const extractedPkgDir = join(testDir, 'package');
44
- // Step 3: Test each bin entry
51
+ // Step 3: Symlink workspace dependencies into node_modules
52
+ if (workspacePackages) {
53
+ const deps = pkgJson.dependencies ?? {};
54
+ const nodeModulesDir = join(extractedPkgDir, 'node_modules');
55
+ for (const [depName, depPath] of Object.entries(workspacePackages)) {
56
+ if (depName in deps) {
57
+ // Handle scoped packages: @scope/name → node_modules/@scope/name
58
+ const segments = depName.split('/');
59
+ if (segments.length === 2) {
60
+ await mkdir(join(nodeModulesDir, segments[0]), { recursive: true });
61
+ }
62
+ else {
63
+ await mkdir(nodeModulesDir, { recursive: true });
64
+ }
65
+ await symlink(depPath, join(nodeModulesDir, depName), 'dir');
66
+ }
67
+ }
68
+ }
69
+ // Step 4: Test each bin entry
45
70
  for (const [binName, binPath] of Object.entries(binEntries)) {
46
71
  const binFullPath = join(extractedPkgDir, binPath);
47
72
  const binTestResult = await spawn(['node', binFullPath, '--version'], extractedPkgDir);
@@ -52,7 +77,7 @@ export async function smokeTestTarball(pkgDir, spawn) {
52
77
  }
53
78
  }
54
79
  finally {
55
- // Step 4: Always clean up temp directory and tarball
80
+ // Step 5: Always clean up temp directory and tarball
56
81
  await rm(testDir, { recursive: true, force: true });
57
82
  await rm(join(pkgDir, tarballName), { force: true });
58
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shazhou/proman-core",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "description": "Core library for TypeScript monorepo management",
6
6
  "keywords": [
@@ -113,7 +113,12 @@ export async function publish(opts: PublishOptions = {}): Promise<void> {
113
113
 
114
114
  // Smoke test: validate tarball before publishing
115
115
  try {
116
- await smokeTestTarball(pkgDir, spawn)
116
+ // Build workspace package map for symlink resolution
117
+ const workspacePackages: Record<string, string> = {}
118
+ for (const pkg of cfg.packages) {
119
+ workspacePackages[pkg.name] = resolve(cwd, pkg.path)
120
+ }
121
+ await smokeTestTarball(pkgDir, spawn, workspacePackages)
117
122
  } catch (err) {
118
123
  const message = (err as Error).message
119
124
  const published = publishablePackages.slice(0, i).map((p) => p.name)
@@ -1,4 +1,4 @@
1
- import { readFile, rm } from 'node:fs/promises'
1
+ import { mkdir, readFile, rm, symlink } from 'node:fs/promises'
2
2
  import { tmpdir } from 'node:os'
3
3
  import { join } from 'node:path'
4
4
  import type { SpawnFn } from './npm.js'
@@ -7,13 +7,29 @@ type PackageJson = {
7
7
  name: string
8
8
  version: string
9
9
  bin?: string | Record<string, string>
10
+ dependencies?: Record<string, string>
10
11
  }
11
12
 
13
+ /**
14
+ * Map of workspace package names → absolute paths on disk.
15
+ * Used to symlink workspace dependencies into the smoke test directory
16
+ * so bin commands can resolve them without npm install.
17
+ */
18
+ export type WorkspacePackages = Record<string, string>
19
+
12
20
  /**
13
21
  * Smoke test a package tarball by extracting it and running bin commands.
14
22
  * Validates that the packaged artifact actually works before publishing.
23
+ *
24
+ * @param workspacePackages - Map of workspace package names to their absolute
25
+ * paths. When provided, workspace dependencies are symlinked into the
26
+ * extracted tarball's node_modules so bin commands can resolve them.
15
27
  */
16
- export async function smokeTestTarball(pkgDir: string, spawn: SpawnFn): Promise<void> {
28
+ export async function smokeTestTarball(
29
+ pkgDir: string,
30
+ spawn: SpawnFn,
31
+ workspacePackages?: WorkspacePackages,
32
+ ): Promise<void> {
17
33
  // Read package.json to check for bin entries
18
34
  const pkgJsonPath = join(pkgDir, 'package.json')
19
35
  const pkgJsonText = await readFile(pkgJsonPath, 'utf8')
@@ -39,10 +55,13 @@ export async function smokeTestTarball(pkgDir: string, spawn: SpawnFn): Promise<
39
55
  throw new Error(`pnpm pack failed: ${packResult.stderr || packResult.stdout}`)
40
56
  }
41
57
 
42
- const tarballName = packResult.stdout.trim()
43
- if (!tarballName) {
44
- throw new Error('pnpm pack did not return tarball filename')
58
+ // pnpm pack outputs verbose info (📦, file list, etc.)
59
+ // Extract the .tgz filename from the output
60
+ const tgzMatch = packResult.stdout.match(/[\w@.-]+\.tgz/)
61
+ if (!tgzMatch) {
62
+ throw new Error(`pnpm pack did not return tarball filename. Output: ${packResult.stdout}`)
45
63
  }
64
+ const tarballName = tgzMatch[0]
46
65
 
47
66
  // Step 2: Extract tarball to temp directory
48
67
  const { mkdtemp } = await import('node:fs/promises')
@@ -59,7 +78,26 @@ export async function smokeTestTarball(pkgDir: string, spawn: SpawnFn): Promise<
59
78
  // pnpm pack creates a 'package/' directory inside the tarball
60
79
  const extractedPkgDir = join(testDir, 'package')
61
80
 
62
- // Step 3: Test each bin entry
81
+ // Step 3: Symlink workspace dependencies into node_modules
82
+ if (workspacePackages) {
83
+ const deps = pkgJson.dependencies ?? {}
84
+ const nodeModulesDir = join(extractedPkgDir, 'node_modules')
85
+
86
+ for (const [depName, depPath] of Object.entries(workspacePackages)) {
87
+ if (depName in deps) {
88
+ // Handle scoped packages: @scope/name → node_modules/@scope/name
89
+ const segments = depName.split('/')
90
+ if (segments.length === 2) {
91
+ await mkdir(join(nodeModulesDir, segments[0] as string), { recursive: true })
92
+ } else {
93
+ await mkdir(nodeModulesDir, { recursive: true })
94
+ }
95
+ await symlink(depPath, join(nodeModulesDir, depName), 'dir')
96
+ }
97
+ }
98
+ }
99
+
100
+ // Step 4: Test each bin entry
63
101
  for (const [binName, binPath] of Object.entries(binEntries)) {
64
102
  const binFullPath = join(extractedPkgDir, binPath)
65
103
  const binTestResult = await spawn(['node', binFullPath, '--version'], extractedPkgDir)
@@ -72,7 +110,7 @@ export async function smokeTestTarball(pkgDir: string, spawn: SpawnFn): Promise<
72
110
  }
73
111
  }
74
112
  } finally {
75
- // Step 4: Always clean up temp directory and tarball
113
+ // Step 5: Always clean up temp directory and tarball
76
114
  await rm(testDir, { recursive: true, force: true })
77
115
  await rm(join(pkgDir, tarballName), { force: true })
78
116
  }