@shazhou/proman-core 0.9.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/CHANGELOG.md +26 -0
- package/LICENSE +18 -0
- package/dist/commands/bump.d.ts +13 -0
- package/dist/commands/bump.d.ts.map +1 -0
- package/dist/commands/bump.js +115 -0
- package/dist/commands/deploy.d.ts +9 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +42 -0
- package/dist/commands/dev.d.ts +15 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +175 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +262 -0
- package/dist/commands/link.d.ts +19 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +155 -0
- package/dist/commands/publish.d.ts +18 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +125 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/load-config.d.ts +6 -0
- package/dist/config/load-config.d.ts.map +1 -0
- package/dist/config/load-config.js +29 -0
- package/dist/config/types.d.ts +17 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +1 -0
- package/dist/config/validate-config.d.ts +7 -0
- package/dist/config/validate-config.d.ts.map +1 -0
- package/dist/config/validate-config.js +72 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/utils/changeset.d.ts +16 -0
- package/dist/utils/changeset.d.ts.map +1 -0
- package/dist/utils/changeset.js +80 -0
- package/dist/utils/fingerprint.d.ts +38 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +182 -0
- package/dist/utils/git.d.ts +23 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +105 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/npm.d.ts +30 -0
- package/dist/utils/npm.d.ts.map +1 -0
- package/dist/utils/npm.js +85 -0
- package/dist/utils/smoke-test.d.ts +7 -0
- package/dist/utils/smoke-test.d.ts.map +1 -0
- package/dist/utils/smoke-test.js +59 -0
- package/dist/utils/version.d.ts +5 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +36 -0
- package/dist/utils/workspace.d.ts +21 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +73 -0
- package/package.json +45 -0
- package/src/commands/bump.ts +131 -0
- package/src/commands/deploy.ts +52 -0
- package/src/commands/dev.ts +214 -0
- package/src/commands/index.ts +7 -0
- package/src/commands/init.integration.test.ts +59 -0
- package/src/commands/init.test.ts +179 -0
- package/src/commands/init.ts +290 -0
- package/src/commands/link.ts +195 -0
- package/src/commands/publish.ts +168 -0
- package/src/config/index.ts +8 -0
- package/src/config/load-config.ts +33 -0
- package/src/config/types.ts +19 -0
- package/src/config/validate-config.ts +81 -0
- package/src/index.ts +29 -0
- package/src/utils/changeset.ts +98 -0
- package/src/utils/fingerprint.ts +199 -0
- package/src/utils/git.ts +119 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/npm.ts +110 -0
- package/src/utils/smoke-test.ts +79 -0
- package/src/utils/version.ts +41 -0
- package/src/utils/workspace.ts +94 -0
- package/tests/build-fingerprint-integration.test.ts +403 -0
- package/tests/bump.test.ts +261 -0
- package/tests/changeset.test.ts +147 -0
- package/tests/deploy.test.ts +98 -0
- package/tests/dev.test.ts +756 -0
- package/tests/fingerprint.test.ts +316 -0
- package/tests/fixtures/api-only/packages/api/.gitkeep +0 -0
- package/tests/fixtures/api-only/proman.yaml +4 -0
- package/tests/fixtures/bad-packages/proman.yaml +1 -0
- package/tests/fixtures/bun-project/packages/a/.gitkeep +0 -0
- package/tests/fixtures/bun-project/proman.yaml +4 -0
- package/tests/fixtures/defaults/proman.yaml +3 -0
- package/tests/fixtures/no-deployable/packages/core/.gitkeep +0 -0
- package/tests/fixtures/no-deployable/packages/mycli/.gitkeep +0 -0
- package/tests/fixtures/no-deployable/proman.yaml +7 -0
- package/tests/fixtures/node-runtime/packages/a/package.json +5 -0
- package/tests/fixtures/node-runtime/proman.yaml +3 -0
- package/tests/fixtures/pnpm-project/packages/a/package.json +1 -0
- package/tests/fixtures/pnpm-project/pnpm-lock.yaml +0 -0
- package/tests/fixtures/pnpm-project/proman.yaml +3 -0
- package/tests/fixtures/typed/packages/api/.gitkeep +0 -0
- package/tests/fixtures/typed/packages/core/.gitkeep +0 -0
- package/tests/fixtures/typed/packages/dashboard/.gitkeep +0 -0
- package/tests/fixtures/typed/packages/mycli/.gitkeep +0 -0
- package/tests/fixtures/typed/proman.yaml +13 -0
- package/tests/fixtures/valid/packages/cli/package.json +5 -0
- package/tests/fixtures/valid/packages/core/package.json +5 -0
- package/tests/fixtures/valid/packages/fs/package.json +5 -0
- package/tests/fixtures/valid/proman.yaml +13 -0
- package/tests/fixtures/webui-only/packages/dashboard/.gitkeep +0 -0
- package/tests/fixtures/webui-only/proman.yaml +4 -0
- package/tests/link.test.ts +419 -0
- package/tests/load-config.test.ts +44 -0
- package/tests/npm.test.ts +199 -0
- package/tests/publish.test.ts +599 -0
- package/tests/smoke-test.test.ts +211 -0
- package/tests/validate-config.test.ts +67 -0
- package/tests/version.test.ts +86 -0
- package/tests/workflow-schema.test.ts +72 -0
- package/tests/workspace.test.ts +160 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +8 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.9.0 — 2026-06-13
|
|
4
|
+
|
|
5
|
+
- Refactor proman into a monorepo with `@shazhou/proman-core` and `@shazhou/proman` packages.
|
|
6
|
+
|
|
7
|
+
**Breaking**: None - CLI interface remains unchanged
|
|
8
|
+
|
|
9
|
+
**New packages**:
|
|
10
|
+
- `@shazhou/proman-core` - Core library with config loader, utils, and command logic as pure functions for programmatic use
|
|
11
|
+
- `@shazhou/proman` - CLI package that parses args and delegates to core functions
|
|
12
|
+
|
|
13
|
+
**Features**:
|
|
14
|
+
- Proman now manages itself as a monorepo (dogfooding)
|
|
15
|
+
- Single-package projects work as one-package monorepos (eliminates need for unirepo support)
|
|
16
|
+
- Core exports are pure functions with no CLI-specific side effects
|
|
17
|
+
- Root `proman.yaml` configures packages array for monorepo management
|
|
18
|
+
|
|
19
|
+
**Migration**: Users only need to upgrade the `@shazhou/proman` CLI package. The core package is automatically installed as a dependency.
|
|
20
|
+
- Migrate tests from root to package-specific directories and fix monorepo dependencies
|
|
21
|
+
|
|
22
|
+
- Migrate all test files from root `tests/` to `packages/core/tests/` and `packages/cli/tests/`
|
|
23
|
+
- Update test imports to reference correct package structure
|
|
24
|
+
- Remove `yaml` dependency from root package.json (now only in core package where it's used)
|
|
25
|
+
- Fix workflow schema test paths to reference root `.workflows/` directory
|
|
26
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xiaoju
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type BumpOptions = {
|
|
2
|
+
type?: 'major' | 'minor' | 'patch';
|
|
3
|
+
cwd?: string;
|
|
4
|
+
now?: () => Date;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Bump package versions independently.
|
|
8
|
+
* --type: bump all packages with the given type.
|
|
9
|
+
* No --type: infer per-package bumps from changesets; only bump packages mentioned in changesets.
|
|
10
|
+
* Returns a map of package name → new version.
|
|
11
|
+
*/
|
|
12
|
+
export declare function bump(opts?: BumpOptions): Promise<Record<string, string>>;
|
|
13
|
+
//# sourceMappingURL=bump.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bump.d.ts","sourceRoot":"","sources":["../../src/commands/bump.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAA;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB,CAAA;AA2BD;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkFlF"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFile, stat, unlink, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { loadConfig } from '../config/load-config.js';
|
|
4
|
+
import { buildChangelogEntry, prependChangelog, readChangesets, } from '../utils/changeset.js';
|
|
5
|
+
import { bumpVersion, inferBump } from '../utils/version.js';
|
|
6
|
+
async function readJson(path) {
|
|
7
|
+
const text = await readFile(path, 'utf8');
|
|
8
|
+
return JSON.parse(text);
|
|
9
|
+
}
|
|
10
|
+
async function writeJson(path, data) {
|
|
11
|
+
await writeFile(path, `${JSON.stringify(data, null, 2)}\n`);
|
|
12
|
+
}
|
|
13
|
+
function formatDate(d) {
|
|
14
|
+
const y = d.getUTCFullYear();
|
|
15
|
+
const m = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
16
|
+
const day = String(d.getUTCDate()).padStart(2, '0');
|
|
17
|
+
return `${y}-${m}-${day}`;
|
|
18
|
+
}
|
|
19
|
+
async function fileExists(path) {
|
|
20
|
+
try {
|
|
21
|
+
await stat(path);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Bump package versions independently.
|
|
30
|
+
* --type: bump all packages with the given type.
|
|
31
|
+
* No --type: infer per-package bumps from changesets; only bump packages mentioned in changesets.
|
|
32
|
+
* Returns a map of package name → new version.
|
|
33
|
+
*/
|
|
34
|
+
export async function bump(opts = {}) {
|
|
35
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
36
|
+
const now = opts.now ?? (() => new Date());
|
|
37
|
+
const cfg = loadConfig(cwd);
|
|
38
|
+
const bumped = {};
|
|
39
|
+
if (opts.type) {
|
|
40
|
+
// Explicit --type: bump all packages
|
|
41
|
+
for (const pkg of cfg.packages) {
|
|
42
|
+
const pkgPath = resolve(cwd, pkg.path, 'package.json');
|
|
43
|
+
const json = await readJson(pkgPath);
|
|
44
|
+
const current = json.version;
|
|
45
|
+
if (!current)
|
|
46
|
+
throw new Error(`missing version in ${pkgPath}`);
|
|
47
|
+
const version = bumpVersion(current, opts.type);
|
|
48
|
+
json.version = version;
|
|
49
|
+
await writeJson(pkgPath, json);
|
|
50
|
+
bumped[pkg.name] = version;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Infer from changesets: per-package independent bump
|
|
55
|
+
const changesets = await readChangesets(cwd);
|
|
56
|
+
if (changesets.length === 0) {
|
|
57
|
+
throw new Error('no --type specified and no pending changesets found');
|
|
58
|
+
}
|
|
59
|
+
const bumpMap = inferBump(changesets);
|
|
60
|
+
if (Object.keys(bumpMap).length === 0) {
|
|
61
|
+
throw new Error('no inferable bump from changeset entries');
|
|
62
|
+
}
|
|
63
|
+
const pkgByName = new Map(cfg.packages.map((p) => [p.name, p]));
|
|
64
|
+
for (const [pkgName, bumpType] of Object.entries(bumpMap)) {
|
|
65
|
+
const pkg = pkgByName.get(pkgName);
|
|
66
|
+
if (!pkg)
|
|
67
|
+
continue; // changeset mentions unknown package, skip
|
|
68
|
+
const pkgPath = resolve(cwd, pkg.path, 'package.json');
|
|
69
|
+
const json = await readJson(pkgPath);
|
|
70
|
+
const current = json.version;
|
|
71
|
+
if (!current)
|
|
72
|
+
throw new Error(`missing version in ${pkgPath}`);
|
|
73
|
+
const version = bumpVersion(current, bumpType);
|
|
74
|
+
json.version = version;
|
|
75
|
+
await writeJson(pkgPath, json);
|
|
76
|
+
bumped[pkgName] = version;
|
|
77
|
+
}
|
|
78
|
+
// Generate CHANGELOG.md per bumped package
|
|
79
|
+
const date = formatDate(now());
|
|
80
|
+
const byPackage = {};
|
|
81
|
+
for (const cs of changesets) {
|
|
82
|
+
for (const pkg of Object.keys(cs.packages)) {
|
|
83
|
+
if (!pkgByName.has(pkg))
|
|
84
|
+
continue;
|
|
85
|
+
const arr = byPackage[pkg] ?? [];
|
|
86
|
+
arr.push(cs);
|
|
87
|
+
byPackage[pkg] = arr;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const [pkgName, version] of Object.entries(bumped)) {
|
|
91
|
+
const list = byPackage[pkgName];
|
|
92
|
+
if (!list || list.length === 0)
|
|
93
|
+
continue;
|
|
94
|
+
const pkg = pkgByName.get(pkgName);
|
|
95
|
+
if (!pkg)
|
|
96
|
+
continue;
|
|
97
|
+
const entry = buildChangelogEntry({
|
|
98
|
+
version,
|
|
99
|
+
date,
|
|
100
|
+
bodies: list.map((c) => c.body),
|
|
101
|
+
});
|
|
102
|
+
const path = resolve(cwd, pkg.path, 'CHANGELOG.md');
|
|
103
|
+
let existing = null;
|
|
104
|
+
if (await fileExists(path)) {
|
|
105
|
+
existing = await readFile(path, 'utf8');
|
|
106
|
+
}
|
|
107
|
+
await writeFile(path, prependChangelog(existing, entry));
|
|
108
|
+
}
|
|
109
|
+
// Delete consumed changeset files
|
|
110
|
+
for (const cs of changesets) {
|
|
111
|
+
await unlink(cs.file);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return bumped;
|
|
115
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SpawnFn } from '../utils/npm.js';
|
|
2
|
+
export type DeployCommandOptions = {
|
|
3
|
+
cwd: string;
|
|
4
|
+
pkg?: string;
|
|
5
|
+
env?: string;
|
|
6
|
+
spawn?: SpawnFn;
|
|
7
|
+
};
|
|
8
|
+
export declare function deploy(opts: DeployCommandOptions): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAExE,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAMD,wBAAsB,MAAM,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCtE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { loadConfig } from '../config/index.js';
|
|
3
|
+
import { defaultSpawn, runOrThrow } from '../utils/npm.js';
|
|
4
|
+
function pnpmExec(bin, ...args) {
|
|
5
|
+
return ['pnpm', 'exec', bin, ...args];
|
|
6
|
+
}
|
|
7
|
+
export async function deploy(opts) {
|
|
8
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
9
|
+
const cwd = resolve(opts.cwd);
|
|
10
|
+
const cfg = loadConfig(cwd);
|
|
11
|
+
let targets = cfg.packages;
|
|
12
|
+
if (opts.pkg !== undefined) {
|
|
13
|
+
const match = cfg.packages.find((p) => p.name === opts.pkg);
|
|
14
|
+
if (!match) {
|
|
15
|
+
throw new Error(`package not found: ${opts.pkg}`);
|
|
16
|
+
}
|
|
17
|
+
if (match.type !== 'webui' && match.type !== 'api') {
|
|
18
|
+
throw new Error(`package ${opts.pkg} (type=${match.type}) is not deployable; cannot deploy non-webui/api packages`);
|
|
19
|
+
}
|
|
20
|
+
targets = [match];
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
targets = cfg.packages.filter((p) => p.type === 'webui' || p.type === 'api');
|
|
24
|
+
}
|
|
25
|
+
for (const pkg of targets) {
|
|
26
|
+
const pkgDir = resolve(cwd, pkg.path);
|
|
27
|
+
let argv;
|
|
28
|
+
if (pkg.type === 'webui') {
|
|
29
|
+
argv = pnpmExec('wrangler', 'pages', 'deploy', 'dist');
|
|
30
|
+
}
|
|
31
|
+
else if (pkg.type === 'api') {
|
|
32
|
+
argv = pnpmExec('wrangler', 'deploy');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (opts.env !== undefined) {
|
|
38
|
+
argv.push('--env', opts.env);
|
|
39
|
+
}
|
|
40
|
+
await runOrThrow(spawn, argv, pkgDir);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type SpawnFn } from '../utils/npm.js';
|
|
2
|
+
export type DevCommandOptions = {
|
|
3
|
+
cwd: string;
|
|
4
|
+
spawn?: SpawnFn;
|
|
5
|
+
/** When provided, enables fingerprint caching.
|
|
6
|
+
* - false: check fingerprint, skip if match
|
|
7
|
+
* - true: always run (--force / CI)
|
|
8
|
+
* - undefined: legacy behavior — always run, no fingerprint logic */
|
|
9
|
+
force?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function build(opts: DevCommandOptions): Promise<void>;
|
|
12
|
+
export declare function runTests(opts: DevCommandOptions): Promise<void>;
|
|
13
|
+
export declare function check(opts: DevCommandOptions): Promise<void>;
|
|
14
|
+
export declare function format(opts: DevCommandOptions): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAUA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAExE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;0EAGsE;IACtE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAMD,wBAAsB,KAAK,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuElE;AAsBD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrE;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBlE;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAInE"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { chmodSync, existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { loadConfig } from '../config/index.js';
|
|
4
|
+
import { computeBuildFingerprints, computeRootFingerprint, fingerprintPath, readFingerprint, writeFingerprint, } from '../utils/fingerprint.js';
|
|
5
|
+
import { defaultSpawn, runOrThrow } from '../utils/npm.js';
|
|
6
|
+
function pnpmExec(bin, ...args) {
|
|
7
|
+
return ['pnpm', 'exec', bin, ...args];
|
|
8
|
+
}
|
|
9
|
+
export async function build(opts) {
|
|
10
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
11
|
+
const cwd = resolve(opts.cwd);
|
|
12
|
+
const cfg = loadConfig(cwd);
|
|
13
|
+
const useFingerprint = opts.force !== undefined;
|
|
14
|
+
const force = opts.force ?? false;
|
|
15
|
+
// Compute fingerprints only when fingerprint caching is enabled
|
|
16
|
+
const fingerprints = useFingerprint ? computeBuildFingerprints(cwd, cfg.packages) : null;
|
|
17
|
+
// Determine which packages to build
|
|
18
|
+
const toRun = [];
|
|
19
|
+
for (let i = 0; i < cfg.packages.length; i++) {
|
|
20
|
+
const pkg = cfg.packages[i];
|
|
21
|
+
const pkgDir = resolve(cwd, pkg.path);
|
|
22
|
+
const fpPath = fingerprintPath(pkgDir, 'build', pkg.name);
|
|
23
|
+
const fpValue = fingerprints?.get(pkg.name) ?? '';
|
|
24
|
+
if (useFingerprint && !force) {
|
|
25
|
+
const stored = readFingerprint(fpPath);
|
|
26
|
+
if (stored === fpValue) {
|
|
27
|
+
console.log(`⏭ build: ${pkg.name} (unchanged)`);
|
|
28
|
+
continue; // skip — fingerprint matches
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
toRun.push({ idx: i, pkgDir, fpPath, fpValue });
|
|
32
|
+
}
|
|
33
|
+
// Execute builds
|
|
34
|
+
for (const { idx, pkgDir } of toRun) {
|
|
35
|
+
const pkg = cfg.packages[idx];
|
|
36
|
+
// Clean output dir + tsbuildinfo before build to prevent stale artifacts.
|
|
37
|
+
// Note: since build fingerprints live inside dist/ (see fingerprintPath()),
|
|
38
|
+
// removing dist/ intentionally invalidates the build cache for this package.
|
|
39
|
+
const outDir = join(pkgDir, 'dist');
|
|
40
|
+
if (existsSync(outDir)) {
|
|
41
|
+
rmSync(outDir, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
const buildInfo = join(pkgDir, 'tsconfig.tsbuildinfo');
|
|
44
|
+
if (existsSync(buildInfo)) {
|
|
45
|
+
rmSync(buildInfo);
|
|
46
|
+
}
|
|
47
|
+
let argv;
|
|
48
|
+
switch (pkg.type) {
|
|
49
|
+
case 'webui':
|
|
50
|
+
argv = pnpmExec('vite', 'build');
|
|
51
|
+
break;
|
|
52
|
+
case 'cli':
|
|
53
|
+
// cli → use package's own build script (may be esbuild, tsc, etc.)
|
|
54
|
+
argv = ['pnpm', 'run', 'build'];
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
// lib | api → tsc --build
|
|
58
|
+
argv = pnpmExec('tsc', '--build');
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
await runOrThrow(spawn, argv, pkgDir);
|
|
62
|
+
// chmod +x bin entries so linked CLIs survive tsc rebuild
|
|
63
|
+
chmodBinEntries(pkgDir);
|
|
64
|
+
}
|
|
65
|
+
// Write fingerprints only after ALL builds succeed (and only when enabled)
|
|
66
|
+
if (useFingerprint) {
|
|
67
|
+
for (const { fpPath, fpValue } of toRun) {
|
|
68
|
+
writeFingerprint(fpPath, fpValue);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function chmodBinEntries(pkgDir) {
|
|
73
|
+
const pkgJsonPath = join(pkgDir, 'package.json');
|
|
74
|
+
if (!existsSync(pkgJsonPath))
|
|
75
|
+
return;
|
|
76
|
+
const json = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
77
|
+
const bin = json.bin;
|
|
78
|
+
if (bin == null)
|
|
79
|
+
return;
|
|
80
|
+
const paths = typeof bin === 'string'
|
|
81
|
+
? [bin]
|
|
82
|
+
: typeof bin === 'object'
|
|
83
|
+
? Object.values(bin)
|
|
84
|
+
: [];
|
|
85
|
+
for (const rel of paths) {
|
|
86
|
+
const abs = resolve(pkgDir, rel);
|
|
87
|
+
if (existsSync(abs)) {
|
|
88
|
+
chmodSync(abs, 0o755);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export async function runTests(opts) {
|
|
93
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
94
|
+
const cwd = resolve(opts.cwd);
|
|
95
|
+
const useFingerprint = opts.force !== undefined;
|
|
96
|
+
const force = opts.force ?? false;
|
|
97
|
+
if (useFingerprint) {
|
|
98
|
+
const fpPath = fingerprintPath(cwd, 'test');
|
|
99
|
+
const fpValue = computeRootFingerprint(cwd, 'test');
|
|
100
|
+
if (!force) {
|
|
101
|
+
const stored = readFingerprint(fpPath);
|
|
102
|
+
if (stored === fpValue) {
|
|
103
|
+
console.log('⏭ test (unchanged)');
|
|
104
|
+
return; // skip
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
await runOrThrow(spawn, pnpmExec('vitest', 'run'), cwd);
|
|
108
|
+
writeFingerprint(fpPath, fpValue);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
await runOrThrow(spawn, pnpmExec('vitest', 'run'), cwd);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export async function check(opts) {
|
|
115
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
116
|
+
const cwd = resolve(opts.cwd);
|
|
117
|
+
const useFingerprint = opts.force !== undefined;
|
|
118
|
+
const force = opts.force ?? false;
|
|
119
|
+
if (useFingerprint) {
|
|
120
|
+
const fpPath = fingerprintPath(cwd, 'check');
|
|
121
|
+
const fpValue = computeRootFingerprint(cwd, 'check');
|
|
122
|
+
if (!force) {
|
|
123
|
+
const stored = readFingerprint(fpPath);
|
|
124
|
+
if (stored === fpValue) {
|
|
125
|
+
console.log('⏭ check (unchanged)');
|
|
126
|
+
return; // skip
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await runOrThrow(spawn, pnpmExec('biome', 'check', '.'), cwd);
|
|
130
|
+
await validateWorkflows(spawn, cwd);
|
|
131
|
+
writeFingerprint(fpPath, fpValue);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
await runOrThrow(spawn, pnpmExec('biome', 'check', '.'), cwd);
|
|
135
|
+
await validateWorkflows(spawn, cwd);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export async function format(opts) {
|
|
139
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
140
|
+
const cwd = resolve(opts.cwd);
|
|
141
|
+
await runOrThrow(spawn, pnpmExec('biome', 'format', '--write', '.'), cwd);
|
|
142
|
+
}
|
|
143
|
+
/** Discover .workflows/*.yaml and validate each with `uwf workflow validate`. Skips if uwf is not installed. */
|
|
144
|
+
async function validateWorkflows(spawn, cwd) {
|
|
145
|
+
const dirs = [join(cwd, '.workflows'), join(cwd, '.workflow')];
|
|
146
|
+
const files = [];
|
|
147
|
+
for (const dir of dirs) {
|
|
148
|
+
if (!existsSync(dir))
|
|
149
|
+
continue;
|
|
150
|
+
for (const entry of readdirSync(dir)) {
|
|
151
|
+
if (entry.endsWith('.yaml') || entry.endsWith('.yml')) {
|
|
152
|
+
files.push(join(dir, entry));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (files.length === 0)
|
|
157
|
+
return;
|
|
158
|
+
// Check if uwf is available
|
|
159
|
+
const { code: uwfCheck } = await spawn(['which', 'uwf'], cwd);
|
|
160
|
+
if (uwfCheck !== 0) {
|
|
161
|
+
console.log('⚠ uwf not installed, skipping workflow validation');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const errors = [];
|
|
165
|
+
for (const file of files) {
|
|
166
|
+
const { code, stderr } = await spawn(['uwf', 'workflow', 'validate', file], cwd);
|
|
167
|
+
if (code !== 0) {
|
|
168
|
+
errors.push(`${file}: ${stderr.trim() || 'validation failed'}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (errors.length > 0) {
|
|
172
|
+
throw new Error(`Workflow validation failed:\n${errors.join('\n')}`);
|
|
173
|
+
}
|
|
174
|
+
console.log(`✓ ${files.length} workflow(s) validated`);
|
|
175
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { type BumpOptions, bump } from './bump.js';
|
|
2
|
+
export { type DeployCommandOptions, deploy } from './deploy.js';
|
|
3
|
+
export { build, check, type DevCommandOptions, format, runTests } from './dev.js';
|
|
4
|
+
export { type InitOptions, init } from './init.js';
|
|
5
|
+
export { type LinkCommandOptions, link, linkStatus, unlink } from './link.js';
|
|
6
|
+
export { type PublishOptions, publish } from './publish.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACjF,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,KAAK,kBAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Export all command functions
|
|
2
|
+
export { bump } from './bump.js';
|
|
3
|
+
export { deploy } from './deploy.js';
|
|
4
|
+
export { build, check, format, runTests } from './dev.js';
|
|
5
|
+
export { init } from './init.js';
|
|
6
|
+
export { link, linkStatus, unlink } from './link.js';
|
|
7
|
+
export { publish } from './publish.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AA6BD,wBAAsB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C3D"}
|