codemodctl 0.1.5 → 0.1.6

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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { analyzeCodeowners } from "./codeowner-analysis-CeRIGuLu.js";
2
+ import { analyzeCodeowners } from "./codeowner-analysis-CcSOX0Ve.js";
3
3
  import { defineCommand, runMain } from "citty";
4
4
  import crypto from "node:crypto";
5
5
  import { $ } from "execa";
@@ -71,29 +71,31 @@ const createCommand = defineCommand({
71
71
  console.error("Error: BUTTERFLOW_API_AUTH_TOKEN environment variable is required");
72
72
  process.exit(1);
73
73
  }
74
- try {
75
- await $`git diff --quiet`;
76
- await $`git diff --cached --quiet`;
77
- console.error("No changes detected, skipping pull request creation.");
78
- process.exit(0);
79
- } catch {
80
- console.log("Changes detected, proceeding with pull request creation...");
81
- }
82
- const taskIdSignature = crypto.createHash("sha256").update(taskId).digest("hex").slice(0, 8);
83
- const codemodBranchName = branchName ? branchName : `codemod-${taskIdSignature}`;
84
- let remoteBaseBranch;
85
- try {
86
- remoteBaseBranch = (await $`git remote show origin`).stdout.match(/HEAD branch: (.+)/)?.[1]?.trim() || "main";
87
- } catch (error) {
88
- console.error("Error: Failed to get remote base branch");
89
- console.error(error);
90
- process.exit(1);
91
- }
92
- console.debug(`Remote base branch: ${remoteBaseBranch}`);
74
+ const prData = { title };
93
75
  if (push) {
76
+ try {
77
+ await $`git diff --quiet`;
78
+ await $`git diff --cached --quiet`;
79
+ console.error("No changes detected, skipping pull request creation.");
80
+ process.exit(0);
81
+ } catch {
82
+ console.log("Changes detected, proceeding with pull request creation...");
83
+ }
84
+ const taskIdSignature = crypto.createHash("sha256").update(taskId).digest("hex").slice(0, 8);
85
+ const codemodBranchName = branchName ? branchName : `codemod-${taskIdSignature}`;
86
+ let remoteBaseBranch;
87
+ try {
88
+ remoteBaseBranch = (await $`git remote show origin`).stdout.match(/HEAD branch: (.+)/)?.[1]?.trim() || "main";
89
+ } catch (error) {
90
+ console.error("Error: Failed to get remote base branch");
91
+ console.error(error);
92
+ process.exit(1);
93
+ }
94
+ if (codemodBranchName) prData.head = codemodBranchName;
95
+ if (remoteBaseBranch) prData.base = remoteBaseBranch;
96
+ console.debug(`Remote base branch: ${remoteBaseBranch}`);
94
97
  try {
95
98
  await $`git checkout -b ${codemodBranchName}`;
96
- console.log(`✅ Created and checked out branch: ${codemodBranchName}`);
97
99
  } catch (error) {
98
100
  console.error("Error: Failed to checkout branch");
99
101
  console.error(error);
@@ -101,7 +103,6 @@ const createCommand = defineCommand({
101
103
  }
102
104
  try {
103
105
  await $`git add .`;
104
- console.log("✅ Added changes to staging");
105
106
  } catch (error) {
106
107
  console.error("Error: Failed to add changes");
107
108
  console.error(error);
@@ -109,7 +110,6 @@ const createCommand = defineCommand({
109
110
  }
110
111
  try {
111
112
  await $`git commit -m ${commitMessage}`;
112
- console.log("✅ Committed changes");
113
113
  } catch (error) {
114
114
  console.error("Error: Failed to commit changes");
115
115
  console.error(error);
@@ -117,19 +117,16 @@ const createCommand = defineCommand({
117
117
  }
118
118
  try {
119
119
  await $`git push origin ${codemodBranchName} --force`;
120
- console.log(`✅ Pushed branch to origin: ${codemodBranchName}`);
120
+ console.log(`Pushed branch to origin: ${codemodBranchName}`);
121
121
  } catch (error) {
122
122
  console.error("Error: Failed to push changes");
123
123
  console.error(error);
124
124
  process.exit(1);
125
125
  }
126
126
  }
127
- const prData = { title };
128
127
  if (body) prData.body = body;
129
- if (codemodBranchName && push) prData.head = codemodBranchName;
130
- else if (head) prData.head = head;
131
- if (base) prData.base = base;
132
- else if (remoteBaseBranch) prData.base = remoteBaseBranch;
128
+ if (head && !prData.head) prData.head = head;
129
+ if (base && !prData.base) prData.base = base;
133
130
  try {
134
131
  console.debug("Creating pull request...");
135
132
  console.debug(`Title: ${title}`);
@@ -97,6 +97,7 @@ const countedFindInFiles = countedPromise(findInFiles);
97
97
  /**
98
98
  * Finds and resolves the CODEOWNERS file path
99
99
  * Searches in common locations: root, .github/, docs/
100
+ * Returns null if no CODEOWNERS file is found
100
101
  */
101
102
  async function findCodeownersFile(projectRoot = process.cwd(), explicitPath) {
102
103
  if (explicitPath) {
@@ -110,7 +111,7 @@ async function findCodeownersFile(projectRoot = process.cwd(), explicitPath) {
110
111
  resolve(projectRoot, "docs", "CODEOWNERS")
111
112
  ];
112
113
  for (const searchPath of searchPaths) if (existsSync(searchPath)) return searchPath;
113
- throw new Error("CODEOWNERS file not found. Please specify path explicitly or ensure CODEOWNERS file exists in project root, .github/, or docs/ folder.");
114
+ return null;
114
115
  }
115
116
  /**
116
117
  * Loads and parses AST-grep rule from YAML file
@@ -159,6 +160,28 @@ async function analyzeFilesByOwner(codeownersPath, rule, projectRoot = process.c
159
160
  return filesByOwner;
160
161
  }
161
162
  /**
163
+ * Analyzes files without codeowner parsing - assigns all files to "unassigned"
164
+ */
165
+ async function analyzeFilesWithoutOwner(rule, projectRoot = process.cwd()) {
166
+ const filesByOwner = /* @__PURE__ */ new Map();
167
+ filesByOwner.set("unassigned", []);
168
+ await countedFindInFiles(Lang.TypeScript, {
169
+ matcher: rule,
170
+ paths: [projectRoot]
171
+ }, (err, matches) => {
172
+ if (err) {
173
+ console.error("AST-grep error:", err);
174
+ return;
175
+ }
176
+ if (!matches || matches.length === 0) return;
177
+ const fileName = matches[0]?.getRoot().filename();
178
+ if (!fileName) return;
179
+ const relativePath = path.relative(projectRoot, fileName);
180
+ filesByOwner.get("unassigned").push(relativePath);
181
+ });
182
+ return filesByOwner;
183
+ }
184
+ /**
162
185
  * Generates shard configuration from team file analysis
163
186
  */
164
187
  function generateShards(filesByOwner, shardSize) {
@@ -169,7 +192,8 @@ function generateShards(filesByOwner, shardSize) {
169
192
  console.log(`Team "${team}" owns ${fileCount} files, creating ${numShards} shards`);
170
193
  for (let i = 1; i <= numShards; i++) allShards.push({
171
194
  team,
172
- shard: `${i}/${numShards}`
195
+ shard: `${i}/${numShards}`,
196
+ shardId: `${team} ${i}/${numShards}`
173
197
  });
174
198
  }
175
199
  return allShards;
@@ -191,11 +215,18 @@ async function analyzeCodeowners(options) {
191
215
  const { shardSize, codeownersPath, rulePath, projectRoot = process.cwd() } = options;
192
216
  const resolvedCodeownersPath = await findCodeownersFile(projectRoot, codeownersPath);
193
217
  const rule = await loadAstGrepRule(rulePath);
194
- console.log(`Analyzing CODEOWNERS file: ${resolvedCodeownersPath}`);
195
- console.log(`Using rule file: ${rulePath}`);
196
- console.log(`Shard size: ${shardSize}`);
197
- console.log("Analyzing files with AST-grep...");
198
- const filesByOwner = await analyzeFilesByOwner(resolvedCodeownersPath, rule, projectRoot);
218
+ let filesByOwner;
219
+ console.debug(`Using rule file: ${rulePath}`);
220
+ console.debug(`Shard size: ${shardSize}`);
221
+ if (resolvedCodeownersPath) {
222
+ console.log(`Analyzing CODEOWNERS file: ${resolvedCodeownersPath}`);
223
+ console.log("Analyzing files with AST-grep...");
224
+ filesByOwner = await analyzeFilesByOwner(resolvedCodeownersPath, rule, projectRoot);
225
+ } else {
226
+ console.log("No CODEOWNERS file found, assigning all files to 'unassigned'");
227
+ console.log("Analyzing files with AST-grep...");
228
+ filesByOwner = await analyzeFilesWithoutOwner(rule, projectRoot);
229
+ }
199
230
  console.log("File analysis completed. Generating shards...");
200
231
  const teams = getTeamFileInfo(filesByOwner);
201
232
  const shards = generateShards(filesByOwner, shardSize);
@@ -209,4 +240,4 @@ async function analyzeCodeowners(options) {
209
240
  }
210
241
 
211
242
  //#endregion
212
- export { analyzeCodeowners, analyzeFilesByOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName };
243
+ export { analyzeCodeowners, analyzeFilesByOwner, analyzeFilesWithoutOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName };
package/dist/index.d.ts CHANGED
@@ -57,6 +57,7 @@ declare function calculateOptimalShardCount(totalFiles: number, targetShardSize:
57
57
  interface ShardResult {
58
58
  team: string;
59
59
  shard: string;
60
+ shardId: string;
60
61
  }
61
62
  interface TeamFileInfo {
62
63
  team: string;
@@ -77,8 +78,9 @@ interface CodeownerAnalysisResult {
77
78
  /**
78
79
  * Finds and resolves the CODEOWNERS file path
79
80
  * Searches in common locations: root, .github/, docs/
81
+ * Returns null if no CODEOWNERS file is found
80
82
  */
81
- declare function findCodeownersFile(projectRoot?: string, explicitPath?: string): Promise<string>;
83
+ declare function findCodeownersFile(projectRoot?: string, explicitPath?: string): Promise<string | null>;
82
84
  /**
83
85
  * Loads and parses AST-grep rule from YAML file
84
86
  */
@@ -91,6 +93,10 @@ declare function normalizeOwnerName(owner: string): string;
91
93
  * Analyzes files and groups them by codeowner team
92
94
  */
93
95
  declare function analyzeFilesByOwner(codeownersPath: string, rule: any, projectRoot?: string): Promise<Map<string, string[]>>;
96
+ /**
97
+ * Analyzes files without codeowner parsing - assigns all files to "unassigned"
98
+ */
99
+ declare function analyzeFilesWithoutOwner(rule: any, projectRoot?: string): Promise<Map<string, string[]>>;
94
100
  /**
95
101
  * Generates shard configuration from team file analysis
96
102
  */
@@ -104,4 +110,4 @@ declare function getTeamFileInfo(filesByOwner: Map<string, string[]>): TeamFileI
104
110
  */
105
111
  declare function analyzeCodeowners(options: CodeownerAnalysisOptions): Promise<CodeownerAnalysisResult>;
106
112
  //#endregion
107
- export { CodeownerAnalysisOptions, CodeownerAnalysisResult, ShardResult, TeamFileInfo, analyzeCodeowners, analyzeFilesByOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName };
113
+ export { CodeownerAnalysisOptions, CodeownerAnalysisResult, ShardResult, TeamFileInfo, analyzeCodeowners, analyzeFilesByOwner, analyzeFilesWithoutOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { analyzeCodeowners, analyzeFilesByOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName } from "./codeowner-analysis-CeRIGuLu.js";
2
+ import { analyzeCodeowners, analyzeFilesByOwner, analyzeFilesWithoutOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName } from "./codeowner-analysis-CcSOX0Ve.js";
3
3
 
4
- export { analyzeCodeowners, analyzeFilesByOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName };
4
+ export { analyzeCodeowners, analyzeFilesByOwner, analyzeFilesWithoutOwner, calculateOptimalShardCount, distributeFilesAcrossShards, findCodeownersFile, fitsInShard, generateShards, getFileHashPosition, getNumericFileNameSha1, getShardForFilename, getTeamFileInfo, loadAstGrepRule, normalizeOwnerName };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemodctl",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "CLI tool and utilities for workflow engine operations, file sharding, and codeowner analysis",
5
5
  "type": "module",
6
6
  "exports": {