codeowners-git 1.2.0 → 1.4.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.
Files changed (3) hide show
  1. package/README.md +34 -4
  2. package/dist/cli.js +84 -48
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,6 +12,8 @@ Managing large-scale migrations in big monorepos with multiple codeowners can be
12
12
  - Creating compact, team-specific branches with only their affected files.
13
13
  - Streamlining the review process with smaller, targeted PRs.
14
14
 
15
+ > **Note:** This tool works with **unstaged files**. Make sure to check if your files are unstaged before proceeding.
16
+
15
17
  https://github.com/user-attachments/assets/7cc0a924-f03e-47f3-baad-63eca9e8e4a8
16
18
 
17
19
  ## Installation
@@ -46,6 +48,18 @@ The tool automatically detects CODEOWNERS files in:
46
48
 
47
49
  ## Commands
48
50
 
51
+ ### `--version`
52
+
53
+ Display the version of codeowners-git.
54
+
55
+ Usage:
56
+
57
+ ```bash
58
+ codeowners-git --version
59
+ # or
60
+ codeowners-git -V
61
+ ```
62
+
49
63
  ### `list`
50
64
 
51
65
  List current CODEOWNERS entries.
@@ -107,7 +121,7 @@ codeowners-git multi-branch [options]
107
121
 
108
122
  Options:
109
123
 
110
- - `--branch, -b` Base branch name (will be prefixed with codeowner name)
124
+ - `--branch, -b` Base branch name (will be suffixed with codeowner name)
111
125
  - `--message, -m` Base commit message (will be suffixed with codeowner name)
112
126
  - `--no-verify, -n` Skips lint-staged and other checks before committing
113
127
  - `--push, -p` Push branches to remote after commit
@@ -115,18 +129,34 @@ Options:
115
129
  - `--upstream, -u` Upstream branch name pattern (defaults to local branch name)
116
130
  - `--force, -f` Force push to remote
117
131
  - `--keep-branch-on-failure, -k` Keep created branches even if operation fails
132
+ - `--default-owner, -d` Default owner to use when no codeowners are found for changed files
133
+ - `--ignore` Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')
134
+ - `--include` Comma-separated patterns to include codeowners (e.g., 'team-_,@org/_')
135
+
136
+ > **Note:** You cannot use both `--ignore` and `--include` options at the same time.
118
137
 
119
138
  Example:
120
139
 
121
140
  ```bash
141
+ # Create branches for all codeowners
122
142
  codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" -p
143
+
144
+ # Exclude specific teams
145
+ codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" --ignore "@ce-orca,@ce-ece"
146
+
147
+ # Include only specific patterns
148
+ codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" --include "@team-*"
149
+
150
+ # Use default owner when no codeowners found
151
+ codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" -d "@default-team"
123
152
  ```
124
153
 
125
154
  This will:
126
155
 
127
- 1. Find all codeowners for the changed files in your repository
128
- 2. For each codeowner (e.g., @team-a, @team-b):
129
- - Create a branch like `team-a/feature/new-feature`
156
+ 1. Find all codeowners for the staged files in your repository
157
+ 2. Apply any ignore/include filters if specified
158
+ 3. For each codeowner (e.g., @team-a, @team-b):
159
+ - Create a branch like `feature/new-feature/team-a`
130
160
  - Commit only the files owned by that team
131
161
  - Add a commit message like "Add new feature - @team-a"
132
162
  - Push each branch to the remote if the `-p` flag is provided
package/dist/cli.js CHANGED
@@ -965,8 +965,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
965
965
  this._exitCallback = (err) => {
966
966
  if (err.code !== "commander.executeSubCommandAsync") {
967
967
  throw err;
968
- } else {
969
- }
968
+ } else {}
970
969
  };
971
970
  }
972
971
  return this;
@@ -2133,8 +2132,7 @@ var require_p_locate = __commonJS((exports, module) => {
2133
2132
  const limit = pLimit(opts.concurrency);
2134
2133
  const items = Array.from(iterable).map((el) => [el, limit(() => Promise.resolve(el).then(tester))]);
2135
2134
  const checkLimit = pLimit(opts.preserveOrder ? 1 : Infinity);
2136
- return Promise.all(items.map((el) => checkLimit(() => finder(el)))).then(() => {
2137
- }).catch((err) => err instanceof EndError ? err.value : Promise.reject(err));
2135
+ return Promise.all(items.map((el) => checkLimit(() => finder(el)))).then(() => {}).catch((err) => err instanceof EndError ? err.value : Promise.reject(err));
2138
2136
  };
2139
2137
  });
2140
2138
 
@@ -2992,8 +2990,7 @@ var require_minimatch = __commonJS((exports, module) => {
2992
2990
  var path = function() {
2993
2991
  try {
2994
2992
  return __require("path");
2995
- } catch (e) {
2996
- }
2993
+ } catch (e) {}
2997
2994
  }() || {
2998
2995
  sep: "/"
2999
2996
  };
@@ -3101,8 +3098,7 @@ var require_minimatch = __commonJS((exports, module) => {
3101
3098
  this.partial = !!options.partial;
3102
3099
  this.make();
3103
3100
  }
3104
- Minimatch.prototype.debug = function() {
3105
- };
3101
+ Minimatch.prototype.debug = function() {};
3106
3102
  Minimatch.prototype.make = make;
3107
3103
  function make() {
3108
3104
  var pattern = this.pattern;
@@ -3224,7 +3220,7 @@ var require_minimatch = __commonJS((exports, module) => {
3224
3220
  }
3225
3221
  }
3226
3222
  for (var i = 0, len = pattern.length, c;i < len && (c = pattern.charAt(i)); i++) {
3227
- this.debug("%s\t%s %s %j", pattern, i, re, c);
3223
+ this.debug("%s %s %s %j", pattern, i, re, c);
3228
3224
  if (escaping && reSpecials[c]) {
3229
3225
  re += "\\" + c;
3230
3226
  escaping = false;
@@ -3243,7 +3239,7 @@ var require_minimatch = __commonJS((exports, module) => {
3243
3239
  case "+":
3244
3240
  case "@":
3245
3241
  case "!":
3246
- this.debug("%s\t%s %s %j <-- stateChar", pattern, i, re, c);
3242
+ this.debug("%s %s %s %j <-- stateChar", pattern, i, re, c);
3247
3243
  if (inClass) {
3248
3244
  this.debug(" in class");
3249
3245
  if (c === "!" && i === classStart + 1)
@@ -3593,8 +3589,7 @@ var require_inherits_browser = __commonJS((exports, module) => {
3593
3589
  module.exports = function inherits(ctor, superCtor) {
3594
3590
  if (superCtor) {
3595
3591
  ctor.super_ = superCtor;
3596
- var TempCtor = function() {
3597
- };
3592
+ var TempCtor = function() {};
3598
3593
  TempCtor.prototype = superCtor.prototype;
3599
3594
  ctor.prototype = new TempCtor;
3600
3595
  ctor.prototype.constructor = ctor;
@@ -5331,8 +5326,7 @@ var require_browser = __commonJS((exports, module) => {
5331
5326
  });
5332
5327
  args.splice(lastC, 0, c);
5333
5328
  }
5334
- exports.log = console.debug || console.log || (() => {
5335
- });
5329
+ exports.log = console.debug || console.log || (() => {});
5336
5330
  function save(namespaces) {
5337
5331
  try {
5338
5332
  if (namespaces) {
@@ -5340,15 +5334,13 @@ var require_browser = __commonJS((exports, module) => {
5340
5334
  } else {
5341
5335
  exports.storage.removeItem("debug");
5342
5336
  }
5343
- } catch (error) {
5344
- }
5337
+ } catch (error) {}
5345
5338
  }
5346
5339
  function load() {
5347
5340
  let r;
5348
5341
  try {
5349
5342
  r = exports.storage.getItem("debug");
5350
- } catch (error) {
5351
- }
5343
+ } catch (error) {}
5352
5344
  if (!r && typeof process !== "undefined" && "env" in process) {
5353
5345
  r = process.env.DEBUG;
5354
5346
  }
@@ -5357,8 +5349,7 @@ var require_browser = __commonJS((exports, module) => {
5357
5349
  function localstorage() {
5358
5350
  try {
5359
5351
  return localStorage;
5360
- } catch (error) {
5361
- }
5352
+ } catch (error) {}
5362
5353
  }
5363
5354
  module.exports = require_common2()(exports);
5364
5355
  var { formatters } = module.exports;
@@ -5419,8 +5410,7 @@ var require_node = __commonJS((exports, module) => {
5419
5410
  exports.save = save;
5420
5411
  exports.load = load;
5421
5412
  exports.useColors = useColors;
5422
- exports.destroy = util.deprecate(() => {
5423
- }, "Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
5413
+ exports.destroy = util.deprecate(() => {}, "Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
5424
5414
  exports.colors = [6, 2, 3, 4, 5, 1];
5425
5415
  try {
5426
5416
  const supportsColor = require_supports_color();
@@ -5504,8 +5494,7 @@ var require_node = __commonJS((exports, module) => {
5504
5494
  221
5505
5495
  ];
5506
5496
  }
5507
- } catch (error) {
5508
- }
5497
+ } catch (error) {}
5509
5498
  exports.inspectOpts = Object.keys(process.env).filter((key) => {
5510
5499
  return /^debug_/i.test(key);
5511
5500
  }).reduce((obj, key) => {
@@ -6633,8 +6622,7 @@ var require_colors = __commonJS((exports, module) => {
6633
6622
  });
6634
6623
  return ret;
6635
6624
  }();
6636
- var proto2 = defineProps(function colors() {
6637
- }, styles3);
6625
+ var proto2 = defineProps(function colors() {}, styles3);
6638
6626
  function applyStyle2() {
6639
6627
  var args = Array.prototype.slice.call(arguments);
6640
6628
  var str = args.map(function(arg) {
@@ -6822,8 +6810,7 @@ var require_cell = __commonJS((exports, module) => {
6822
6810
  let content = utils.truncate(this.content, 10, this.truncate);
6823
6811
  if (!lineNum) {
6824
6812
  info(`${this.y}-${this.x}: ${this.rowSpan - lineNum}x${this.colSpan} Cell ${content}`);
6825
- } else {
6826
- }
6813
+ } else {}
6827
6814
  let padLen = Math.max(this.height - this.lines.length, 0);
6828
6815
  let padTop;
6829
6816
  switch (this.vAlign) {
@@ -6957,18 +6944,15 @@ var require_cell = __commonJS((exports, module) => {
6957
6944
  }
6958
6945
 
6959
6946
  class ColSpanCell {
6960
- constructor() {
6961
- }
6947
+ constructor() {}
6962
6948
  draw(lineNum) {
6963
6949
  if (typeof lineNum === "number") {
6964
6950
  debug2(`${this.y}-${this.x}: 1x1 ColSpanCell`);
6965
6951
  }
6966
6952
  return "";
6967
6953
  }
6968
- init() {
6969
- }
6970
- mergeTableOptions() {
6971
- }
6954
+ init() {}
6955
+ mergeTableOptions() {}
6972
6956
  }
6973
6957
 
6974
6958
  class RowSpanCell {
@@ -6991,8 +6975,7 @@ var require_cell = __commonJS((exports, module) => {
6991
6975
  debug2(`${this.y}-${this.x}: 1x${this.colSpan} RowSpanCell for ${this.originalCell.content}`);
6992
6976
  return this.originalCell.draw(this.offset + 1 + lineNum);
6993
6977
  }
6994
- mergeTableOptions() {
6995
- }
6978
+ mergeTableOptions() {}
6996
6979
  }
6997
6980
  function firstDefined(...args) {
6998
6981
  return args.filter((v) => v !== undefined && v !== null).shift();
@@ -10260,8 +10243,7 @@ var objectToString;
10260
10243
  var init_util = __esm({
10261
10244
  "src/lib/utils/util.ts"() {
10262
10245
  NULL = "\x00";
10263
- NOOP = () => {
10264
- };
10246
+ NOOP = () => {};
10265
10247
  objectToString = Object.prototype.toString.call.bind(Object.prototype.toString);
10266
10248
  }
10267
10249
  });
@@ -14458,8 +14440,7 @@ for (const model of usedModels) {
14458
14440
  }
14459
14441
  };
14460
14442
  }
14461
- var proto = Object.defineProperties(() => {
14462
- }, {
14443
+ var proto = Object.defineProperties(() => {}, {
14463
14444
  ...styles2,
14464
14445
  level: {
14465
14446
  enumerable: true,
@@ -14547,6 +14528,7 @@ var log = {
14547
14528
  success: (message) => console.log(source_default.green(`✓ ${message}`)),
14548
14529
  error: (message) => console.error(source_default.red(`✗ ${message}`)),
14549
14530
  info: (message) => console.log(source_default.bold(`ℹ ${message}`)),
14531
+ warn: (message) => console.warn(source_default.yellow(`⚠ ${message}`)),
14550
14532
  header: (message) => console.log(source_default.bold.cyan(`
14551
14533
  ${message}`)),
14552
14534
  file: (path) => console.log(`- ${source_default.dim(path)}`),
@@ -14706,9 +14688,22 @@ var pushBranch = async (branchName, {
14706
14688
  };
14707
14689
 
14708
14690
  // src/utils/codeowners.ts
14709
- var codeowners = new import_codeowners.default;
14691
+ var codeowners;
14692
+ var getCodeownersInstance = () => {
14693
+ if (!codeowners) {
14694
+ try {
14695
+ codeowners = new import_codeowners.default;
14696
+ } catch (error) {
14697
+ return {
14698
+ getOwner: () => []
14699
+ };
14700
+ }
14701
+ }
14702
+ return codeowners;
14703
+ };
14710
14704
  var getOwner = (filePath) => {
14711
- const owner = codeowners.getOwner(filePath);
14705
+ const instance = getCodeownersInstance();
14706
+ const owner = instance.getOwner(filePath);
14712
14707
  return owner;
14713
14708
  };
14714
14709
  var getOwnerFiles = async (owner) => {
@@ -14793,7 +14788,8 @@ var branch = async (options) => {
14793
14788
  log.info(`Currently on branch: ${originalBranch}`);
14794
14789
  filesToCommit = await getOwnerFiles(options.owner);
14795
14790
  if (filesToCommit.length <= 0) {
14796
- throw new Error(`No files found for ${options.owner}`);
14791
+ log.warn(`No files found for ${options.owner}. Skipping branch creation.`);
14792
+ return;
14797
14793
  }
14798
14794
  log.file(`Files to be committed:
14799
14795
  ${filesToCommit.join(`
@@ -14858,11 +14854,15 @@ var branch = async (options) => {
14858
14854
  };
14859
14855
 
14860
14856
  // src/commands/multi-branch.ts
14857
+ var import_micromatch2 = __toESM(require_micromatch(), 1);
14861
14858
  var multiBranch = async (options) => {
14862
14859
  try {
14863
14860
  if (!options.branch || !options.message) {
14864
14861
  throw new Error("Missing required options for multi-branch creation");
14865
14862
  }
14863
+ if (options.ignore && options.include) {
14864
+ throw new Error("Cannot use both --ignore and --include options at the same time");
14865
+ }
14866
14866
  log.info("Starting multi-branch creation process...");
14867
14867
  const changedFiles = await getChangedFiles();
14868
14868
  if (changedFiles.length === 0) {
@@ -14875,11 +14875,36 @@ var multiBranch = async (options) => {
14875
14875
  ownerSet.add(owner);
14876
14876
  }
14877
14877
  }
14878
- const codeowners2 = Array.from(ownerSet);
14878
+ let codeowners2 = Array.from(ownerSet);
14879
14879
  if (codeowners2.length === 0) {
14880
- throw new Error("No codeowners found for the changed files");
14880
+ log.warn("No codeowners found for the changed files");
14881
+ if (options.defaultOwner) {
14882
+ log.info(`Using default owner: ${options.defaultOwner}`);
14883
+ codeowners2.push(options.defaultOwner);
14884
+ } else {
14885
+ log.info("Continuing without creating any branches (use --default-owner to specify a fallback)");
14886
+ return;
14887
+ }
14888
+ } else {
14889
+ log.info(`Found ${codeowners2.length} codeowners: ${codeowners2.join(", ")}`);
14890
+ }
14891
+ if (options.ignore || options.include) {
14892
+ const originalCount = codeowners2.length;
14893
+ if (options.ignore) {
14894
+ const ignorePatterns = options.ignore.split(",").map((p) => p.trim());
14895
+ codeowners2 = codeowners2.filter((owner) => !import_micromatch2.default.isMatch(owner, ignorePatterns));
14896
+ log.info(`Filtered out ${originalCount - codeowners2.length} codeowners using ignore patterns: ${ignorePatterns.join(", ")}`);
14897
+ } else if (options.include) {
14898
+ const includePatterns = options.include.split(",").map((p) => p.trim());
14899
+ codeowners2 = codeowners2.filter((owner) => import_micromatch2.default.isMatch(owner, includePatterns));
14900
+ log.info(`Filtered to ${codeowners2.length} codeowners using include patterns: ${includePatterns.join(", ")}`);
14901
+ }
14902
+ if (codeowners2.length === 0) {
14903
+ log.warn("No codeowners left after filtering");
14904
+ return;
14905
+ }
14906
+ log.info(`Processing ${codeowners2.length} codeowners after filtering: ${codeowners2.join(", ")}`);
14881
14907
  }
14882
- log.info(`Found ${codeowners2.length} codeowners: ${codeowners2.join(", ")}`);
14883
14908
  const results = {
14884
14909
  success: [],
14885
14910
  failure: []
@@ -14921,10 +14946,21 @@ var multiBranch = async (options) => {
14921
14946
  }
14922
14947
  };
14923
14948
 
14949
+ // src/commands/version.ts
14950
+ import { readFileSync } from "fs";
14951
+ import { fileURLToPath } from "url";
14952
+ import { dirname, join } from "path";
14953
+ function getVersion() {
14954
+ const __filename2 = fileURLToPath(import.meta.url);
14955
+ const __dirname2 = dirname(__filename2);
14956
+ const packageJson = JSON.parse(readFileSync(join(__dirname2, "../../package.json"), "utf8"));
14957
+ return packageJson.version;
14958
+ }
14959
+
14924
14960
  // src/cli.ts
14925
14961
  var program2 = new Command;
14926
- program2.name("codeowners").description("CLI tool for grouping and managing staged files by CODEOWNERS");
14962
+ program2.name("codeowners").description("CLI tool for grouping and managing staged files by CODEOWNERS").version(getVersion());
14927
14963
  program2.command("list").description("Lists all git changed files by CODEOWNER").option("-o, --owner <owner>", "Filter by specific code owner").option("-i, --include <patterns>", "Filter by owner patterns").action(listCodeowners);
14928
14964
  program2.command("branch").description("Create new branch with codeowner changes").requiredOption("-o, --owner <owner>", "Code owner name").requiredOption("-b, --branch <branch>", "Branch name").requiredOption("-m, --message <message>", "Commit message").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branch to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep the created branch even if operation fails").action(branch);
14929
- program2.command("multi-branch").description("Create branches for all codeowners").requiredOption("-b, --branch <branch>", "Base branch name (will be suffixed with codeowner name)").requiredOption("-m, --message <message>", "Base commit message (will be suffixed with codeowner name)").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branches to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name pattern (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep created branches even if operation fails").action(multiBranch);
14965
+ program2.command("multi-branch").description("Create branches for all codeowners").requiredOption("-b, --branch <branch>", "Base branch name (will be suffixed with codeowner name)").requiredOption("-m, --message <message>", "Base commit message (will be suffixed with codeowner name)").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branches to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name pattern (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep created branches even if operation fails").option("-d, --default-owner <defaultOwner>", "Default owner to use when no codeowners are found for changed files").option("--ignore <patterns>", "Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')").option("--include <patterns>", "Comma-separated patterns to include codeowners (e.g., 'team-*,@org/*')").action(multiBranch);
14930
14966
  program2.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeowners-git",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "module": "src/cli.ts",
5
5
  "type": "module",
6
6
  "private": false,