@oddessentials/repo-standards 4.1.0 → 4.3.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/dist/cli.cjs +116 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +13 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +86 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -7
- package/dist/index.js.map +1 -1
- package/package.json +9 -4
- package/scripts/build.ts +172 -0
- package/scripts/detect-bazel.test.ts +61 -0
- package/scripts/detect-bazel.ts +82 -0
- package/scripts/generate-instructions.ts +174 -0
- package/scripts/generate-standards.ts +247 -0
- package/scripts/sync-manifest-version.cjs +32 -0
- package/scripts/validate-schema.ts +289 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// scripts/detect-bazel.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Standalone Bazel detection utility.
|
|
5
|
+
* Does NOT require Bazel to be installed.
|
|
6
|
+
* Only checks repo-root markers to avoid false positives from vendored deps.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
|
|
12
|
+
export interface BazelDetectionResult {
|
|
13
|
+
/** Whether Bazel markers were detected at repo root */
|
|
14
|
+
detected: boolean;
|
|
15
|
+
/** Bazel mode: bzlmod (MODULE.bazel) or workspace (WORKSPACE*) */
|
|
16
|
+
mode?: "bzlmod" | "workspace";
|
|
17
|
+
/** List of detected marker files */
|
|
18
|
+
markers: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Root-level markers that indicate a Bazel repo */
|
|
22
|
+
const ROOT_MARKERS = {
|
|
23
|
+
bzlmod: ["MODULE.bazel"],
|
|
24
|
+
workspace: ["WORKSPACE.bazel", "WORKSPACE"],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Optional markers that support Bazel but don't trigger detection alone */
|
|
28
|
+
const OPTIONAL_MARKERS = [".bazelrc", ".bazelversion"];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Detect Bazel at repo root only.
|
|
32
|
+
* Does NOT scan subdirectories for BUILD files (avoids vendored deps false positives).
|
|
33
|
+
*
|
|
34
|
+
* @param repoRoot - Absolute path to repository root
|
|
35
|
+
* @returns Detection result with mode and found markers
|
|
36
|
+
*/
|
|
37
|
+
export function detectBazel(repoRoot: string): BazelDetectionResult {
|
|
38
|
+
const foundMarkers: string[] = [];
|
|
39
|
+
let mode: "bzlmod" | "workspace" | undefined;
|
|
40
|
+
|
|
41
|
+
// Check bzlmod first (preferred)
|
|
42
|
+
for (const marker of ROOT_MARKERS.bzlmod) {
|
|
43
|
+
if (fs.existsSync(path.join(repoRoot, marker))) {
|
|
44
|
+
foundMarkers.push(marker);
|
|
45
|
+
mode = "bzlmod";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check workspace if no bzlmod
|
|
50
|
+
if (!mode) {
|
|
51
|
+
for (const marker of ROOT_MARKERS.workspace) {
|
|
52
|
+
if (fs.existsSync(path.join(repoRoot, marker))) {
|
|
53
|
+
foundMarkers.push(marker);
|
|
54
|
+
mode = "workspace";
|
|
55
|
+
break; // Only need one workspace marker
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check optional markers (don't affect detection, just informational)
|
|
61
|
+
for (const marker of OPTIONAL_MARKERS) {
|
|
62
|
+
if (fs.existsSync(path.join(repoRoot, marker))) {
|
|
63
|
+
foundMarkers.push(marker);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
detected: mode !== undefined,
|
|
69
|
+
mode,
|
|
70
|
+
markers: foundMarkers,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// CLI entrypoint for manual testing
|
|
75
|
+
if (
|
|
76
|
+
import.meta.url.startsWith("file:") &&
|
|
77
|
+
process.argv[1]?.includes("detect-bazel")
|
|
78
|
+
) {
|
|
79
|
+
const targetDir = process.argv[2] || process.cwd();
|
|
80
|
+
const result = detectBazel(path.resolve(targetDir));
|
|
81
|
+
console.log(JSON.stringify(result, null, 2));
|
|
82
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// scripts/generate-instructions.ts
|
|
2
|
+
//
|
|
3
|
+
// Generates instructions.md from a stack+CI specific JSON file.
|
|
4
|
+
// Usage: npx ts-node-esm scripts/generate-instructions.ts [config-file]
|
|
5
|
+
// Example: npm run generate:instructions standards.python.github-actions.json
|
|
6
|
+
// Default: config/standards.typescript-js.github-actions.json
|
|
7
|
+
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
interface StackHints {
|
|
12
|
+
exampleTools?: string[];
|
|
13
|
+
exampleConfigFiles?: string[];
|
|
14
|
+
notes?: string;
|
|
15
|
+
verification?: string;
|
|
16
|
+
requiredFiles?: string[];
|
|
17
|
+
optionalFiles?: string[];
|
|
18
|
+
requiredScripts?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ChecklistItem {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
description: string;
|
|
25
|
+
ciHints?: Record<string, { job?: string; stage?: string }>;
|
|
26
|
+
stack?: StackHints;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface StackChecklistJson {
|
|
30
|
+
version: number;
|
|
31
|
+
stack: string;
|
|
32
|
+
stackLabel: string;
|
|
33
|
+
ciSystems: string[];
|
|
34
|
+
checklist: {
|
|
35
|
+
core: ChecklistItem[];
|
|
36
|
+
recommended: ChecklistItem[];
|
|
37
|
+
optionalEnhancements: ChecklistItem[];
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generate 2-5 high-level bullets from a checklist item.
|
|
43
|
+
*/
|
|
44
|
+
function generateBullets(item: ChecklistItem): string[] {
|
|
45
|
+
const bullets: string[] = [];
|
|
46
|
+
const stack = item.stack;
|
|
47
|
+
|
|
48
|
+
// Bullet 1: Core purpose from description
|
|
49
|
+
bullets.push(item.description);
|
|
50
|
+
|
|
51
|
+
// Bullet 2: Required files if any
|
|
52
|
+
if (stack?.requiredFiles?.length) {
|
|
53
|
+
const files = stack.requiredFiles.join(", ");
|
|
54
|
+
bullets.push(`Ensure ${files} exists in the repository.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Bullet 3: Optional files mention
|
|
58
|
+
if (stack?.optionalFiles?.length) {
|
|
59
|
+
const files = stack.optionalFiles.slice(0, 3).join(", ");
|
|
60
|
+
const more = stack.optionalFiles.length > 3 ? " and others" : "";
|
|
61
|
+
bullets.push(`Consider adding ${files}${more} if applicable.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Bullet 4: Required scripts
|
|
65
|
+
if (stack?.requiredScripts?.length) {
|
|
66
|
+
const scripts = stack.requiredScripts.map((s) => `\`${s}\``).join(", ");
|
|
67
|
+
bullets.push(`Define a ${scripts} script or equivalent command.`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Bullet 5: Notes-based guidance (if short enough)
|
|
71
|
+
if (stack?.notes && stack.notes.length < 150) {
|
|
72
|
+
bullets.push(stack.notes);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Limit to 5 bullets
|
|
76
|
+
return bullets.slice(0, 5);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function generateSection(title: string, items: ChecklistItem[]): string {
|
|
80
|
+
if (items.length === 0) return "";
|
|
81
|
+
|
|
82
|
+
const lines: string[] = [];
|
|
83
|
+
lines.push(`## ${title}`);
|
|
84
|
+
lines.push("");
|
|
85
|
+
|
|
86
|
+
for (const item of items) {
|
|
87
|
+
lines.push(`### ${item.label}`);
|
|
88
|
+
lines.push("");
|
|
89
|
+
|
|
90
|
+
const bullets = generateBullets(item);
|
|
91
|
+
for (const bullet of bullets) {
|
|
92
|
+
lines.push(`- ${bullet}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function main() {
|
|
101
|
+
const rootDir = process.cwd();
|
|
102
|
+
|
|
103
|
+
// Accept config file as CLI argument, default to TypeScript+GitHub Actions
|
|
104
|
+
const configArg = process.argv[2];
|
|
105
|
+
let inputPath: string;
|
|
106
|
+
|
|
107
|
+
if (configArg) {
|
|
108
|
+
// If argument provided, resolve relative to config/ or as absolute
|
|
109
|
+
if (path.isAbsolute(configArg)) {
|
|
110
|
+
inputPath = configArg;
|
|
111
|
+
} else if (
|
|
112
|
+
configArg.startsWith("config/") ||
|
|
113
|
+
configArg.startsWith("config\\")
|
|
114
|
+
) {
|
|
115
|
+
inputPath = path.join(rootDir, configArg);
|
|
116
|
+
} else {
|
|
117
|
+
inputPath = path.join(rootDir, "config", configArg);
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
inputPath = path.join(
|
|
121
|
+
rootDir,
|
|
122
|
+
"config",
|
|
123
|
+
"standards.typescript-js.github-actions.json",
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Derive output filename from input (e.g., standards.python.json -> instructions.python.md)
|
|
128
|
+
const inputBasename = path.basename(inputPath, ".json");
|
|
129
|
+
const outputBasename = inputBasename.replace(/^standards\./, "instructions.");
|
|
130
|
+
const outputPath = path.join(rootDir, `${outputBasename}.md`);
|
|
131
|
+
|
|
132
|
+
if (!fs.existsSync(inputPath)) {
|
|
133
|
+
console.error(`Input file not found: ${inputPath}`);
|
|
134
|
+
console.error("Usage: npm run generate:instructions [config-file]");
|
|
135
|
+
console.error(
|
|
136
|
+
"Example: npm run generate:instructions standards.python.github-actions.json",
|
|
137
|
+
);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const raw = fs.readFileSync(inputPath, "utf8");
|
|
142
|
+
const data: StackChecklistJson = JSON.parse(raw);
|
|
143
|
+
|
|
144
|
+
const lines: string[] = [];
|
|
145
|
+
|
|
146
|
+
// Header
|
|
147
|
+
lines.push("# Repository Standards Instructions");
|
|
148
|
+
lines.push("");
|
|
149
|
+
lines.push(`> Auto-generated from \`${path.relative(rootDir, inputPath)}\``);
|
|
150
|
+
lines.push(`> Stack: ${data.stackLabel} | CI: ${data.ciSystems.join(", ")}`);
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push(
|
|
153
|
+
"This document provides high-level guidance for an autonomous coding agent to bring a repository into compliance with the defined standards.",
|
|
154
|
+
);
|
|
155
|
+
lines.push("");
|
|
156
|
+
|
|
157
|
+
// Generate sections
|
|
158
|
+
lines.push(generateSection("Core Requirements", data.checklist.core));
|
|
159
|
+
lines.push(
|
|
160
|
+
generateSection("Recommended Practices", data.checklist.recommended),
|
|
161
|
+
);
|
|
162
|
+
lines.push(
|
|
163
|
+
generateSection(
|
|
164
|
+
"Optional Enhancements",
|
|
165
|
+
data.checklist.optionalEnhancements,
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Write output
|
|
170
|
+
fs.writeFileSync(outputPath, lines.join("\n"));
|
|
171
|
+
console.log(`Wrote ${outputPath}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main();
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// scripts/generate-standards.ts
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
type StackId = "typescript-js" | "csharp-dotnet" | "python" | "rust" | "go";
|
|
7
|
+
type CiSystem = "azure-devops" | "github-actions";
|
|
8
|
+
|
|
9
|
+
interface StackMeta {
|
|
10
|
+
label: string;
|
|
11
|
+
languageFamily: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface CiHintsForSystem {
|
|
15
|
+
stage?: string;
|
|
16
|
+
job?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type CiHints = Partial<Record<CiSystem, CiHintsForSystem>>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Bazel execution hints for individual checklist items.
|
|
23
|
+
* Commands are actual Bazel invocations (e.g., "bazel test //..."),
|
|
24
|
+
* NOT assumed pattern labels.
|
|
25
|
+
*/
|
|
26
|
+
interface BazelHints {
|
|
27
|
+
/** Bazel commands to run (e.g., "bazel test //...", "bazel run //tools/lint") */
|
|
28
|
+
commands?: string[];
|
|
29
|
+
/** Recommended target conventions (documentation only, not assumed to exist) */
|
|
30
|
+
recommendedTargets?: string[];
|
|
31
|
+
/** Usage notes for this check in Bazel context */
|
|
32
|
+
notes?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface StackHints {
|
|
36
|
+
exampleTools?: string[];
|
|
37
|
+
exampleConfigFiles?: string[];
|
|
38
|
+
notes?: string;
|
|
39
|
+
// Human‑readable verification instructions
|
|
40
|
+
verification?: string;
|
|
41
|
+
// Machine‑readable additions (all optional)
|
|
42
|
+
requiredFiles?: string[];
|
|
43
|
+
optionalFiles?: string[];
|
|
44
|
+
requiredScripts?: string[];
|
|
45
|
+
// Either-or file compliance: at least one of these files must exist
|
|
46
|
+
anyOfFiles?: string[];
|
|
47
|
+
// Version pinning guidance for deterministic CI
|
|
48
|
+
pinningNotes?: string;
|
|
49
|
+
machineCheck?: {
|
|
50
|
+
command: string;
|
|
51
|
+
expectExitCode?: number;
|
|
52
|
+
description?: string;
|
|
53
|
+
};
|
|
54
|
+
// Bazel execution hints (v3+)
|
|
55
|
+
bazelHints?: BazelHints;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface ChecklistItemMaster {
|
|
59
|
+
id: string;
|
|
60
|
+
label: string;
|
|
61
|
+
description: string;
|
|
62
|
+
appliesTo: {
|
|
63
|
+
stacks: StackId[];
|
|
64
|
+
ciSystems?: CiSystem[];
|
|
65
|
+
};
|
|
66
|
+
ciHints?: CiHints;
|
|
67
|
+
stackHints?: Partial<Record<StackId, StackHints>>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface ChecklistSection {
|
|
71
|
+
core: ChecklistItemMaster[];
|
|
72
|
+
recommended: ChecklistItemMaster[];
|
|
73
|
+
optionalEnhancements: ChecklistItemMaster[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface MigrationStep {
|
|
77
|
+
step: number;
|
|
78
|
+
title: string;
|
|
79
|
+
description: string;
|
|
80
|
+
focusIds?: string[];
|
|
81
|
+
notes?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface Meta {
|
|
85
|
+
defaultCoverageThreshold?: number;
|
|
86
|
+
complexityChecks?: {
|
|
87
|
+
enabledByDefault?: boolean;
|
|
88
|
+
description?: string;
|
|
89
|
+
};
|
|
90
|
+
qualityGatePolicy?: {
|
|
91
|
+
preferSoftFailOnLegacy?: boolean;
|
|
92
|
+
description?: string;
|
|
93
|
+
};
|
|
94
|
+
migrationGuide?: MigrationStep[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface MasterJson {
|
|
98
|
+
version: number;
|
|
99
|
+
meta?: Meta;
|
|
100
|
+
ciSystems: CiSystem[];
|
|
101
|
+
stacks: Record<StackId, StackMeta>;
|
|
102
|
+
checklist: ChecklistSection;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface StackItem {
|
|
106
|
+
id: string;
|
|
107
|
+
label: string;
|
|
108
|
+
description: string;
|
|
109
|
+
ciHints?: CiHints;
|
|
110
|
+
// For the filtered file, this is the single stack’s hints including verification
|
|
111
|
+
stack?: StackHints;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface StackChecklistJson {
|
|
115
|
+
version: number;
|
|
116
|
+
stack: StackId;
|
|
117
|
+
stackLabel: string;
|
|
118
|
+
ciSystems: CiSystem[];
|
|
119
|
+
meta?: Meta;
|
|
120
|
+
checklist: {
|
|
121
|
+
core: StackItem[];
|
|
122
|
+
recommended: StackItem[];
|
|
123
|
+
optionalEnhancements: StackItem[];
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function filterSectionForStackAndCi(
|
|
128
|
+
items: ChecklistItemMaster[],
|
|
129
|
+
stack: StackId,
|
|
130
|
+
ciSystem?: CiSystem,
|
|
131
|
+
): StackItem[] {
|
|
132
|
+
return items
|
|
133
|
+
.filter((item) => {
|
|
134
|
+
if (!item.appliesTo.stacks.includes(stack)) return false;
|
|
135
|
+
|
|
136
|
+
if (ciSystem && item.appliesTo.ciSystems) {
|
|
137
|
+
return item.appliesTo.ciSystems.includes(ciSystem);
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
})
|
|
141
|
+
.map((item) => {
|
|
142
|
+
const stackHint = item.stackHints?.[stack];
|
|
143
|
+
|
|
144
|
+
const result: StackItem = {
|
|
145
|
+
id: item.id,
|
|
146
|
+
label: item.label,
|
|
147
|
+
description: item.description,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (item.ciHints) {
|
|
151
|
+
if (ciSystem) {
|
|
152
|
+
const perSystem = item.ciHints[ciSystem];
|
|
153
|
+
if (perSystem) {
|
|
154
|
+
result.ciHints = { [ciSystem]: perSystem };
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
result.ciHints = item.ciHints;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (stackHint) {
|
|
162
|
+
// Includes exampleTools, exampleConfigFiles, notes, verification
|
|
163
|
+
result.stack = stackHint;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function generateStackJson(
|
|
171
|
+
master: MasterJson,
|
|
172
|
+
stack: StackId,
|
|
173
|
+
ciSystem?: CiSystem,
|
|
174
|
+
): StackChecklistJson {
|
|
175
|
+
const stackMeta = master.stacks[stack];
|
|
176
|
+
const ciSystems = ciSystem ? [ciSystem] : master.ciSystems;
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
version: master.version,
|
|
180
|
+
stack,
|
|
181
|
+
stackLabel: stackMeta?.label ?? stack,
|
|
182
|
+
ciSystems,
|
|
183
|
+
meta: master.meta,
|
|
184
|
+
checklist: {
|
|
185
|
+
core: filterSectionForStackAndCi(master.checklist.core, stack, ciSystem),
|
|
186
|
+
recommended: filterSectionForStackAndCi(
|
|
187
|
+
master.checklist.recommended,
|
|
188
|
+
stack,
|
|
189
|
+
ciSystem,
|
|
190
|
+
),
|
|
191
|
+
optionalEnhancements: filterSectionForStackAndCi(
|
|
192
|
+
master.checklist.optionalEnhancements,
|
|
193
|
+
stack,
|
|
194
|
+
ciSystem,
|
|
195
|
+
),
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// --- entrypoint ---
|
|
201
|
+
const rootDir = path.join(process.cwd());
|
|
202
|
+
const masterPath = path.join(rootDir, "config", "standards.json");
|
|
203
|
+
|
|
204
|
+
const raw = fs.readFileSync(masterPath, "utf8");
|
|
205
|
+
const master: MasterJson = JSON.parse(raw);
|
|
206
|
+
|
|
207
|
+
// args: stack [ciSystem]
|
|
208
|
+
// args: stack [ciSystem]
|
|
209
|
+
const STACK_ALIASES: Record<string, StackId> = {
|
|
210
|
+
dotnet: "csharp-dotnet",
|
|
211
|
+
csharp: "csharp-dotnet",
|
|
212
|
+
ts: "typescript-js",
|
|
213
|
+
js: "typescript-js",
|
|
214
|
+
python: "python",
|
|
215
|
+
py: "python",
|
|
216
|
+
"typescript-js": "typescript-js",
|
|
217
|
+
"csharp-dotnet": "csharp-dotnet",
|
|
218
|
+
rust: "rust",
|
|
219
|
+
rs: "rust",
|
|
220
|
+
go: "go",
|
|
221
|
+
golang: "go",
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const rawArg = process.argv[2] || "typescript-js";
|
|
225
|
+
const targetStack = STACK_ALIASES[rawArg.toLowerCase()];
|
|
226
|
+
|
|
227
|
+
if (!targetStack) {
|
|
228
|
+
console.error(`Unknown stack: ${rawArg}`);
|
|
229
|
+
console.error(
|
|
230
|
+
`Available stacks: ${["typescript-js", "csharp-dotnet", "python", "rust", "go"].join(", ")}`,
|
|
231
|
+
);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const targetCiSystem = process.argv[3] as CiSystem | undefined;
|
|
236
|
+
|
|
237
|
+
const stackJson = generateStackJson(master, targetStack, targetCiSystem);
|
|
238
|
+
|
|
239
|
+
const outDir = path.join(rootDir, "config");
|
|
240
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
241
|
+
|
|
242
|
+
const ciSuffix = targetCiSystem ? `.${targetCiSystem}` : "";
|
|
243
|
+
const outPath = path.join(outDir, `standards.${targetStack}${ciSuffix}.json`);
|
|
244
|
+
|
|
245
|
+
fs.writeFileSync(outPath, JSON.stringify(stackJson, null, 2) + "\n");
|
|
246
|
+
|
|
247
|
+
console.log(`Wrote ${outPath}`);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syncs manifest.json version with package.json
|
|
3
|
+
* Called by semantic-release exec plugin during release
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/semantic-release/exec
|
|
6
|
+
*/
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const { execSync } = require("child_process");
|
|
10
|
+
|
|
11
|
+
const pkgPath = path.join(process.cwd(), "package.json");
|
|
12
|
+
const manifestPath = path.join(process.cwd(), "manifest.json");
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(manifestPath)) {
|
|
15
|
+
console.log("[sync-manifest] No manifest.json found, skipping");
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
20
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
21
|
+
|
|
22
|
+
manifest.version = pkg.version;
|
|
23
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
24
|
+
|
|
25
|
+
// Run prettier to ensure consistent formatting
|
|
26
|
+
try {
|
|
27
|
+
execSync("npx prettier --write manifest.json", { stdio: "inherit" });
|
|
28
|
+
} catch {
|
|
29
|
+
console.log("[sync-manifest] prettier not available, skipping format");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(`[sync-manifest] Updated manifest.json to ${pkg.version}`);
|