@outfitter/tooling 0.2.1 → 0.2.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/.markdownlint-cli2.jsonc +55 -55
- package/README.md +30 -3
- package/biome.json +79 -72
- package/dist/cli/check-boundary-invocations.d.ts +34 -0
- package/dist/cli/check-boundary-invocations.js +14 -0
- package/dist/cli/check-bunup-registry.d.ts +36 -0
- package/dist/cli/check-bunup-registry.js +12 -0
- package/dist/cli/check-changeset.d.ts +64 -0
- package/dist/cli/check-changeset.js +14 -0
- package/dist/cli/check-clean-tree.d.ts +36 -0
- package/dist/cli/check-clean-tree.js +14 -0
- package/dist/cli/check-exports.d.ts +2 -0
- package/dist/cli/check-exports.js +12 -0
- package/dist/cli/check-readme-imports.d.ts +60 -0
- package/dist/cli/check-readme-imports.js +194 -0
- package/dist/cli/check.js +2 -1
- package/dist/cli/fix.js +2 -1
- package/dist/cli/index.js +1106 -17
- package/dist/cli/init.js +2 -1
- package/dist/cli/pre-push.d.ts +34 -1
- package/dist/cli/pre-push.js +15 -2
- package/dist/cli/upgrade-bun.js +2 -1
- package/dist/index.d.ts +110 -35
- package/dist/index.js +23 -8
- package/dist/registry/build.d.ts +6 -0
- package/dist/registry/build.js +32 -13
- package/dist/registry/index.js +1 -0
- package/dist/registry/schema.js +1 -0
- package/dist/shared/@outfitter/{tooling-xx1146e3.js → tooling-0x5q15ec.js} +2 -1
- package/dist/shared/@outfitter/tooling-1y8w5ahg.js +70 -0
- package/dist/shared/@outfitter/tooling-3w8vr2w3.js +94 -0
- package/dist/shared/@outfitter/tooling-8sd32ts6.js +277 -0
- package/dist/shared/@outfitter/{tooling-s4eqq91d.js → tooling-9errkcvk.js} +2 -1
- package/dist/shared/@outfitter/tooling-9vs606gq.d.ts +3 -0
- package/dist/shared/@outfitter/{tooling-75j500dv.js → tooling-9yzd08v1.js} +10 -6
- package/dist/shared/@outfitter/tooling-ctmgnap5.js +19 -0
- package/dist/shared/@outfitter/tooling-dvwh9qve.js +4 -0
- package/dist/shared/@outfitter/{tooling-xaxdr9da.js → tooling-mxwc1n8w.js} +13 -3
- package/dist/shared/@outfitter/tooling-q0d60xb3.d.ts +58 -0
- package/dist/shared/@outfitter/tooling-r9976n43.js +100 -0
- package/dist/shared/@outfitter/tooling-t17gnh9b.js +78 -0
- package/dist/shared/@outfitter/tooling-tf22zt9p.js +226 -0
- package/dist/shared/chunk-3s189drz.js +4 -0
- package/dist/shared/chunk-6a7tjcgm.js +193 -0
- package/dist/shared/chunk-8aenrm6f.js +18 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +8 -0
- package/lefthook.yml +5 -7
- package/package.json +122 -121
- package/registry/registry.json +78 -76
- package/tsconfig.preset.bun.json +5 -5
- package/tsconfig.preset.json +33 -33
- package/dist/shared/@outfitter/tooling-qm7jeg0d.js +0 -99
package/dist/cli/init.js
CHANGED
package/dist/cli/pre-push.d.ts
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if branch is a TDD RED phase branch
|
|
3
|
+
*/
|
|
4
|
+
declare function isRedPhaseBranch(branch: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Check if branch is a scaffold branch
|
|
7
|
+
*/
|
|
8
|
+
declare function isScaffoldBranch(branch: string): boolean;
|
|
9
|
+
declare function isTestOnlyPath(path: string): boolean;
|
|
10
|
+
declare function areFilesTestOnly(paths: readonly string[]): boolean;
|
|
11
|
+
interface PushChangedFiles {
|
|
12
|
+
readonly files: readonly string[];
|
|
13
|
+
readonly deterministic: boolean;
|
|
14
|
+
readonly source: "upstream" | "baseRef" | "undetermined";
|
|
15
|
+
}
|
|
16
|
+
declare function canBypassRedPhaseByChangedFiles(changedFiles: PushChangedFiles): boolean;
|
|
17
|
+
type ScriptMap = Readonly<Record<string, string | undefined>>;
|
|
18
|
+
type VerificationPlan = {
|
|
19
|
+
readonly ok: true;
|
|
20
|
+
readonly scripts: readonly string[];
|
|
21
|
+
readonly source: "verify:ci" | "fallback";
|
|
22
|
+
} | {
|
|
23
|
+
readonly ok: false;
|
|
24
|
+
readonly error: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Derive strict pre-push verification from package scripts.
|
|
28
|
+
*
|
|
29
|
+
* Priority:
|
|
30
|
+
* 1) `verify:ci`
|
|
31
|
+
* 2) fallback sequence: `typecheck`, `check|lint`, `build`, `test`
|
|
32
|
+
*/
|
|
33
|
+
declare function createVerificationPlan(scripts: ScriptMap): VerificationPlan;
|
|
1
34
|
interface PrePushOptions {
|
|
2
35
|
force?: boolean;
|
|
3
36
|
}
|
|
@@ -5,4 +38,4 @@ interface PrePushOptions {
|
|
|
5
38
|
* Main pre-push command
|
|
6
39
|
*/
|
|
7
40
|
declare function runPrePush(options?: PrePushOptions): Promise<void>;
|
|
8
|
-
export { runPrePush, PrePushOptions };
|
|
41
|
+
export { runPrePush, isTestOnlyPath, isScaffoldBranch, isRedPhaseBranch, createVerificationPlan, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, PrePushOptions };
|
package/dist/cli/pre-push.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
+
areFilesTestOnly,
|
|
4
|
+
canBypassRedPhaseByChangedFiles,
|
|
5
|
+
createVerificationPlan,
|
|
6
|
+
isRedPhaseBranch,
|
|
7
|
+
isScaffoldBranch,
|
|
8
|
+
isTestOnlyPath,
|
|
3
9
|
runPrePush
|
|
4
|
-
} from "../shared/@outfitter/tooling-
|
|
10
|
+
} from "../shared/@outfitter/tooling-8sd32ts6.js";
|
|
11
|
+
import"../shared/@outfitter/tooling-dvwh9qve.js";
|
|
5
12
|
export {
|
|
6
|
-
runPrePush
|
|
13
|
+
runPrePush,
|
|
14
|
+
isTestOnlyPath,
|
|
15
|
+
isScaffoldBranch,
|
|
16
|
+
isRedPhaseBranch,
|
|
17
|
+
createVerificationPlan,
|
|
18
|
+
canBypassRedPhaseByChangedFiles,
|
|
19
|
+
areFilesTestOnly
|
|
7
20
|
};
|
package/dist/cli/upgrade-bun.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,36 +1,111 @@
|
|
|
1
|
-
import "
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
|
|
1
|
+
import { ZodType } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* File entry in a block.
|
|
4
|
+
*/
|
|
5
|
+
interface FileEntry {
|
|
6
|
+
/** Destination path relative to project root */
|
|
7
|
+
path: string;
|
|
8
|
+
/** File contents (embedded in registry) */
|
|
9
|
+
content: string;
|
|
10
|
+
/** Whether to chmod +x after copying */
|
|
11
|
+
executable?: boolean | undefined;
|
|
12
|
+
/** Whether to process as a template (future) */
|
|
13
|
+
template?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Schema for a file entry in a block.
|
|
17
|
+
* Represents a file that will be copied to the user's project.
|
|
18
|
+
*/
|
|
19
|
+
declare const FileEntrySchema: ZodType<FileEntry>;
|
|
20
|
+
/**
|
|
21
|
+
* Block in the registry.
|
|
22
|
+
*/
|
|
23
|
+
interface Block {
|
|
24
|
+
/** Block name (matches the key in blocks record) */
|
|
25
|
+
name: string;
|
|
26
|
+
/** Human-readable description */
|
|
27
|
+
description: string;
|
|
28
|
+
/** Files included in this block */
|
|
29
|
+
files?: FileEntry[] | undefined;
|
|
30
|
+
/** npm dependencies to add to package.json */
|
|
31
|
+
dependencies?: Record<string, string> | undefined;
|
|
32
|
+
/** npm devDependencies to add to package.json */
|
|
33
|
+
devDependencies?: Record<string, string> | undefined;
|
|
34
|
+
/** Other blocks this block extends (for composite blocks) */
|
|
35
|
+
extends?: string[] | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Schema for a block in the registry.
|
|
39
|
+
* A block is a collection of related files that can be added together.
|
|
40
|
+
*/
|
|
41
|
+
declare const BlockSchema: ZodType<Block>;
|
|
42
|
+
/**
|
|
43
|
+
* Complete registry structure.
|
|
44
|
+
*/
|
|
45
|
+
interface Registry {
|
|
46
|
+
/** Registry schema version */
|
|
47
|
+
version: string;
|
|
48
|
+
/** Map of block name to block definition */
|
|
49
|
+
blocks: Record<string, Block>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Schema for the complete registry.
|
|
53
|
+
* Contains all available blocks with their files and metadata.
|
|
54
|
+
*/
|
|
55
|
+
declare const RegistrySchema: ZodType<Registry>;
|
|
56
|
+
/**
|
|
57
|
+
* Block definition used in the build script.
|
|
58
|
+
* Specifies how to collect source files into a block.
|
|
59
|
+
*/
|
|
60
|
+
interface BlockDefinition {
|
|
61
|
+
/** Human-readable description */
|
|
62
|
+
description: string;
|
|
63
|
+
/** Source file paths (relative to repo root) */
|
|
64
|
+
files?: string[];
|
|
65
|
+
/** Remap source paths to destination paths */
|
|
66
|
+
remap?: Record<string, string>;
|
|
67
|
+
/** npm dependencies */
|
|
68
|
+
dependencies?: Record<string, string>;
|
|
69
|
+
/** npm devDependencies */
|
|
70
|
+
devDependencies?: Record<string, string>;
|
|
71
|
+
/** Other blocks this block extends */
|
|
72
|
+
extends?: string[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Configuration for the registry build.
|
|
76
|
+
*/
|
|
77
|
+
interface RegistryBuildConfig {
|
|
78
|
+
/** Registry schema version */
|
|
79
|
+
version: string;
|
|
80
|
+
/** Block definitions */
|
|
81
|
+
blocks: Record<string, BlockDefinition>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Result of adding a block to a project.
|
|
85
|
+
*/
|
|
86
|
+
interface AddBlockResult {
|
|
87
|
+
/** Files that were created */
|
|
88
|
+
created: string[];
|
|
89
|
+
/** Files that were skipped (already exist) */
|
|
90
|
+
skipped: string[];
|
|
91
|
+
/** Files that were overwritten (with --force) */
|
|
92
|
+
overwritten: string[];
|
|
93
|
+
/** Dependencies added to package.json */
|
|
94
|
+
dependencies: Record<string, string>;
|
|
95
|
+
/** devDependencies added to package.json */
|
|
96
|
+
devDependencies: Record<string, string>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Options for the add command.
|
|
100
|
+
*/
|
|
101
|
+
interface AddBlockOptions {
|
|
102
|
+
/** Overwrite existing files */
|
|
103
|
+
force?: boolean;
|
|
104
|
+
/** Show what would be added without making changes */
|
|
105
|
+
dryRun?: boolean;
|
|
106
|
+
/** Working directory (defaults to cwd) */
|
|
107
|
+
cwd?: string;
|
|
108
|
+
}
|
|
109
|
+
/** Package version, read from package.json at load time. */
|
|
110
|
+
declare const VERSION: string;
|
|
36
111
|
export { VERSION, RegistrySchema, RegistryBuildConfig, Registry, FileEntrySchema, FileEntry, BlockSchema, BlockDefinition, Block, AddBlockResult, AddBlockOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import"./shared/@outfitter/tooling-kcvs6mys.js";
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./shared/@outfitter/tooling-g83d0kjv.js";
|
|
2
|
+
VERSION
|
|
3
|
+
} from "./shared/chunk-8aenrm6f.js";
|
|
4
|
+
import"./shared/chunk-3s189drz.js";
|
|
8
5
|
|
|
9
|
-
//
|
|
10
|
-
|
|
6
|
+
// src/registry/schema.ts
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
var FileEntrySchema = z.object({
|
|
9
|
+
path: z.string().min(1),
|
|
10
|
+
content: z.string(),
|
|
11
|
+
executable: z.boolean().optional(),
|
|
12
|
+
template: z.boolean().optional()
|
|
13
|
+
});
|
|
14
|
+
var BlockSchema = z.object({
|
|
15
|
+
name: z.string().min(1),
|
|
16
|
+
description: z.string().min(1),
|
|
17
|
+
files: z.array(FileEntrySchema).optional(),
|
|
18
|
+
dependencies: z.record(z.string()).optional(),
|
|
19
|
+
devDependencies: z.record(z.string()).optional(),
|
|
20
|
+
extends: z.array(z.string()).optional()
|
|
21
|
+
});
|
|
22
|
+
var RegistrySchema = z.object({
|
|
23
|
+
version: z.string(),
|
|
24
|
+
blocks: z.record(BlockSchema)
|
|
25
|
+
});
|
|
11
26
|
export {
|
|
12
27
|
VERSION,
|
|
13
28
|
RegistrySchema,
|
package/dist/registry/build.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
import"../shared/@outfitter/tooling-dvwh9qve.js";
|
|
3
4
|
|
|
4
5
|
// packages/tooling/src/registry/build.ts
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
existsSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
statSync,
|
|
11
|
+
writeFileSync
|
|
12
|
+
} from "fs";
|
|
6
13
|
import { dirname, join } from "path";
|
|
14
|
+
function log(message) {
|
|
15
|
+
process.stdout.write(`${message}
|
|
16
|
+
`);
|
|
17
|
+
}
|
|
7
18
|
function findRepoRoot(startDir) {
|
|
8
19
|
let dir = startDir;
|
|
9
20
|
while (dir !== "/") {
|
|
@@ -76,27 +87,30 @@ var REGISTRY_CONFIG = {
|
|
|
76
87
|
blocks: {
|
|
77
88
|
claude: {
|
|
78
89
|
description: "Claude Code settings and hooks for automated formatting",
|
|
79
|
-
files: [
|
|
80
|
-
".claude/settings.json",
|
|
81
|
-
".claude/hooks/format-code-on-stop.sh"
|
|
82
|
-
]
|
|
90
|
+
files: [".claude/settings.json", ".claude/hooks/format-code-on-stop.sh"]
|
|
83
91
|
},
|
|
84
92
|
biome: {
|
|
85
93
|
description: "Biome linter/formatter configuration via Ultracite",
|
|
86
94
|
files: ["packages/tooling/biome.json"],
|
|
87
95
|
remap: { "packages/tooling/biome.json": "biome.json" },
|
|
88
|
-
devDependencies: { ultracite: "^7.
|
|
96
|
+
devDependencies: { ultracite: "^7.1.1" }
|
|
89
97
|
},
|
|
90
98
|
lefthook: {
|
|
91
99
|
description: "Git hooks via Lefthook for pre-commit and pre-push",
|
|
92
100
|
files: ["packages/tooling/lefthook.yml"],
|
|
93
101
|
remap: { "packages/tooling/lefthook.yml": ".lefthook.yml" },
|
|
94
|
-
devDependencies: {
|
|
102
|
+
devDependencies: {
|
|
103
|
+
"@outfitter/tooling": "^0.2.1",
|
|
104
|
+
lefthook: "^2.0.16",
|
|
105
|
+
ultracite: "^7.1.1"
|
|
106
|
+
}
|
|
95
107
|
},
|
|
96
108
|
markdownlint: {
|
|
97
109
|
description: "Markdown linting configuration via markdownlint-cli2",
|
|
98
110
|
files: ["packages/tooling/.markdownlint-cli2.jsonc"],
|
|
99
|
-
remap: {
|
|
111
|
+
remap: {
|
|
112
|
+
"packages/tooling/.markdownlint-cli2.jsonc": ".markdownlint-cli2.jsonc"
|
|
113
|
+
}
|
|
100
114
|
},
|
|
101
115
|
bootstrap: {
|
|
102
116
|
description: "Project bootstrap script for installing tools and dependencies",
|
|
@@ -113,16 +127,21 @@ function main() {
|
|
|
113
127
|
const repoRoot = findRepoRoot(scriptDir);
|
|
114
128
|
const outputDir = join(repoRoot, "packages/tooling/registry");
|
|
115
129
|
const outputPath = join(outputDir, "registry.json");
|
|
116
|
-
|
|
130
|
+
log(`Building registry from: ${repoRoot}`);
|
|
117
131
|
if (!existsSync(outputDir)) {
|
|
118
132
|
mkdirSync(outputDir, { recursive: true });
|
|
119
133
|
}
|
|
120
134
|
const registry = buildRegistry(REGISTRY_CONFIG, repoRoot);
|
|
121
|
-
writeFileSync(outputPath, JSON.stringify(registry, null,
|
|
135
|
+
writeFileSync(outputPath, `${JSON.stringify(registry, null, "\t")}
|
|
122
136
|
`);
|
|
123
137
|
const blockCount = Object.keys(registry.blocks).length;
|
|
124
138
|
const fileCount = Object.values(registry.blocks).flatMap((b) => b.files ?? []).length;
|
|
125
|
-
|
|
126
|
-
|
|
139
|
+
log(`\u2713 Generated ${outputPath}`);
|
|
140
|
+
log(` ${blockCount} blocks, ${fileCount} files embedded`);
|
|
141
|
+
}
|
|
142
|
+
if (import.meta.main) {
|
|
143
|
+
main();
|
|
127
144
|
}
|
|
128
|
-
|
|
145
|
+
export {
|
|
146
|
+
REGISTRY_CONFIG
|
|
147
|
+
};
|
package/dist/registry/index.js
CHANGED
package/dist/registry/schema.js
CHANGED
|
@@ -9,7 +9,8 @@ function buildCheckCommand(options) {
|
|
|
9
9
|
}
|
|
10
10
|
async function runCheck(paths = []) {
|
|
11
11
|
const cmd = buildCheckCommand({ paths });
|
|
12
|
-
|
|
12
|
+
process.stdout.write(`Running: bun x ${cmd.join(" ")}
|
|
13
|
+
`);
|
|
13
14
|
const proc = Bun.spawn(["bun", "x", ...cmd], {
|
|
14
15
|
stdio: ["inherit", "inherit", "inherit"]
|
|
15
16
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/check-clean-tree.ts
|
|
3
|
+
function parseGitDiff(diffOutput) {
|
|
4
|
+
return diffOutput.split(`
|
|
5
|
+
`).map((line) => line.trim()).filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
function parseUntrackedFiles(lsOutput) {
|
|
8
|
+
return lsOutput.split(`
|
|
9
|
+
`).map((line) => line.trim()).filter(Boolean);
|
|
10
|
+
}
|
|
11
|
+
function isCleanTree(status) {
|
|
12
|
+
return status.modified.length === 0 && status.untracked.length === 0;
|
|
13
|
+
}
|
|
14
|
+
var COLORS = {
|
|
15
|
+
reset: "\x1B[0m",
|
|
16
|
+
red: "\x1B[31m",
|
|
17
|
+
green: "\x1B[32m",
|
|
18
|
+
dim: "\x1B[2m"
|
|
19
|
+
};
|
|
20
|
+
async function runCheckCleanTree(options = {}) {
|
|
21
|
+
const pathArgs = options.paths ?? [];
|
|
22
|
+
const diffResult = Bun.spawnSync(["git", "diff", "HEAD", "--name-only", "--", ...pathArgs], { stderr: "pipe" });
|
|
23
|
+
if (diffResult.exitCode !== 0) {
|
|
24
|
+
process.stderr.write(`Failed to run git diff
|
|
25
|
+
`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const modified = parseGitDiff(diffResult.stdout.toString());
|
|
29
|
+
const lsResult = Bun.spawnSync(["git", "ls-files", "--others", "--exclude-standard", "--", ...pathArgs], { stderr: "pipe" });
|
|
30
|
+
if (lsResult.exitCode !== 0) {
|
|
31
|
+
process.stderr.write(`Failed to run git ls-files
|
|
32
|
+
`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
const untracked = parseUntrackedFiles(lsResult.stdout.toString());
|
|
36
|
+
const clean = modified.length === 0 && untracked.length === 0;
|
|
37
|
+
const status = { clean, modified, untracked };
|
|
38
|
+
if (status.clean) {
|
|
39
|
+
process.stdout.write(`${COLORS.green}Working tree is clean.${COLORS.reset}
|
|
40
|
+
`);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
process.stderr.write(`${COLORS.red}Working tree is dirty after verification:${COLORS.reset}
|
|
44
|
+
|
|
45
|
+
`);
|
|
46
|
+
if (modified.length > 0) {
|
|
47
|
+
process.stderr.write(`Modified files:
|
|
48
|
+
`);
|
|
49
|
+
for (const file of modified) {
|
|
50
|
+
process.stderr.write(` ${COLORS.dim}M${COLORS.reset} ${file}
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (untracked.length > 0) {
|
|
55
|
+
process.stderr.write(`Untracked files:
|
|
56
|
+
`);
|
|
57
|
+
for (const file of untracked) {
|
|
58
|
+
process.stderr.write(` ${COLORS.dim}?${COLORS.reset} ${file}
|
|
59
|
+
`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
process.stderr.write(`
|
|
63
|
+
This likely means a build step produced uncommitted changes.
|
|
64
|
+
`);
|
|
65
|
+
process.stderr.write(`Commit these changes or add them to .gitignore.
|
|
66
|
+
`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { parseGitDiff, parseUntrackedFiles, isCleanTree, runCheckCleanTree };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/check-changeset.ts
|
|
3
|
+
function getChangedPackagePaths(files) {
|
|
4
|
+
const packageNames = new Set;
|
|
5
|
+
const pattern = /^packages\/([^/]+)\/src\//;
|
|
6
|
+
for (const file of files) {
|
|
7
|
+
const match = pattern.exec(file);
|
|
8
|
+
if (match?.[1]) {
|
|
9
|
+
packageNames.add(match[1]);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return [...packageNames].sort();
|
|
13
|
+
}
|
|
14
|
+
function getChangedChangesetFiles(files) {
|
|
15
|
+
const pattern = /^\.changeset\/([^/]+\.md)$/;
|
|
16
|
+
const results = [];
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const match = pattern.exec(file);
|
|
19
|
+
if (match?.[1] && match[1] !== "README.md") {
|
|
20
|
+
results.push(match[1]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return results.sort();
|
|
24
|
+
}
|
|
25
|
+
function checkChangesetRequired(changedPackages, changesetFiles) {
|
|
26
|
+
if (changedPackages.length === 0) {
|
|
27
|
+
return { ok: true, missingFor: [] };
|
|
28
|
+
}
|
|
29
|
+
if (changesetFiles.length > 0) {
|
|
30
|
+
return { ok: true, missingFor: [] };
|
|
31
|
+
}
|
|
32
|
+
return { ok: false, missingFor: changedPackages };
|
|
33
|
+
}
|
|
34
|
+
var COLORS = {
|
|
35
|
+
reset: "\x1B[0m",
|
|
36
|
+
red: "\x1B[31m",
|
|
37
|
+
green: "\x1B[32m",
|
|
38
|
+
yellow: "\x1B[33m",
|
|
39
|
+
blue: "\x1B[34m",
|
|
40
|
+
dim: "\x1B[2m"
|
|
41
|
+
};
|
|
42
|
+
async function runCheckChangeset(options = {}) {
|
|
43
|
+
if (options.skip || process.env["NO_CHANGESET"] === "1") {
|
|
44
|
+
process.stdout.write(`${COLORS.dim}check-changeset skipped (NO_CHANGESET=1)${COLORS.reset}
|
|
45
|
+
`);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
if (process.env["GITHUB_EVENT_NAME"] === "push") {
|
|
49
|
+
process.stdout.write(`${COLORS.dim}check-changeset skipped (push event)${COLORS.reset}
|
|
50
|
+
`);
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
const cwd = process.cwd();
|
|
54
|
+
let changedFiles;
|
|
55
|
+
try {
|
|
56
|
+
const proc = Bun.spawnSync(["git", "diff", "--name-only", "origin/main...HEAD"], { cwd });
|
|
57
|
+
if (proc.exitCode !== 0) {
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
changedFiles = proc.stdout.toString().trim().split(`
|
|
61
|
+
`).filter((line) => line.length > 0);
|
|
62
|
+
} catch {
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
const changedPackages = getChangedPackagePaths(changedFiles);
|
|
66
|
+
if (changedPackages.length === 0) {
|
|
67
|
+
process.stdout.write(`${COLORS.green}No package source changes detected.${COLORS.reset}
|
|
68
|
+
`);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
const changesetFiles = getChangedChangesetFiles(changedFiles);
|
|
72
|
+
const check = checkChangesetRequired(changedPackages, changesetFiles);
|
|
73
|
+
if (check.ok) {
|
|
74
|
+
process.stdout.write(`${COLORS.green}Changeset found for ${changedPackages.length} changed package(s).${COLORS.reset}
|
|
75
|
+
`);
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
process.stderr.write(`${COLORS.red}Missing changeset!${COLORS.reset}
|
|
79
|
+
|
|
80
|
+
`);
|
|
81
|
+
process.stderr.write(`The following packages have source changes but no changeset:
|
|
82
|
+
|
|
83
|
+
`);
|
|
84
|
+
for (const pkg of check.missingFor) {
|
|
85
|
+
process.stderr.write(` ${COLORS.yellow}@outfitter/${pkg}${COLORS.reset}
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
process.stderr.write(`
|
|
89
|
+
Run ${COLORS.blue}bun run changeset${COLORS.reset} to add a changeset, ` + `or add the ${COLORS.blue}no-changeset${COLORS.reset} label to skip.
|
|
90
|
+
`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { getChangedPackagePaths, getChangedChangesetFiles, checkChangesetRequired, runCheckChangeset };
|