codeowners-git 1.1.0 → 1.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/README.md +61 -3
- package/dist/cli.js +275 -64
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@ 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
|
-
|
|
16
|
-
|
|
15
|
+
> **Note:** This tool works with **unstaged files**. Make sure to check if your files are unstaged before proceeding.
|
|
17
16
|
|
|
17
|
+
https://github.com/user-attachments/assets/7cc0a924-f03e-47f3-baad-63eca9e8e4a8
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
@@ -85,13 +85,70 @@ Options:
|
|
|
85
85
|
- `--branch, -b` Specify branch pattern
|
|
86
86
|
- `--message, -m` Commit message for changes
|
|
87
87
|
- `--no-verify, -n` Skips lint-staged and other checks before committing
|
|
88
|
+
- `--push, -p` Push branch to remote after commit
|
|
89
|
+
- `--remote, -r` Remote name to push to (default: "origin")
|
|
90
|
+
- `--upstream, -u` Upstream branch name (defaults to local branch name)
|
|
91
|
+
- `--force, -f` Force push to remote
|
|
92
|
+
- `--keep-branch-on-failure, -k` Keep the created branch even if operation fails
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
codeowners-git branch -o @myteam -b "feature/new-feature" -m "Add new feature" -p
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `multi-branch`
|
|
101
|
+
|
|
102
|
+
Create branches for all codeowners with changes.
|
|
103
|
+
|
|
104
|
+
Usage:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
codeowners-git multi-branch [options]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
|
|
112
|
+
- `--branch, -b` Base branch name (will be suffixed with codeowner name)
|
|
113
|
+
- `--message, -m` Base commit message (will be suffixed with codeowner name)
|
|
114
|
+
- `--no-verify, -n` Skips lint-staged and other checks before committing
|
|
115
|
+
- `--push, -p` Push branches to remote after commit
|
|
116
|
+
- `--remote, -r` Remote name to push to (default: "origin")
|
|
117
|
+
- `--upstream, -u` Upstream branch name pattern (defaults to local branch name)
|
|
118
|
+
- `--force, -f` Force push to remote
|
|
119
|
+
- `--keep-branch-on-failure, -k` Keep created branches even if operation fails
|
|
120
|
+
- `--default-owner, -d` Default owner to use when no codeowners are found for changed files
|
|
121
|
+
- `--ignore` Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')
|
|
122
|
+
- `--include` Comma-separated patterns to include codeowners (e.g., 'team-_,@org/_')
|
|
123
|
+
|
|
124
|
+
> **Note:** You cannot use both `--ignore` and `--include` options at the same time.
|
|
88
125
|
|
|
89
126
|
Example:
|
|
90
127
|
|
|
91
128
|
```bash
|
|
92
|
-
|
|
129
|
+
# Create branches for all codeowners
|
|
130
|
+
codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" -p
|
|
131
|
+
|
|
132
|
+
# Exclude specific teams
|
|
133
|
+
codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" --ignore "@ce-orca,@ce-ece"
|
|
134
|
+
|
|
135
|
+
# Include only specific patterns
|
|
136
|
+
codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" --include "@team-*"
|
|
137
|
+
|
|
138
|
+
# Use default owner when no codeowners found
|
|
139
|
+
codeowners-git multi-branch -b "feature/new-feature" -m "Add new feature" -d "@default-team"
|
|
93
140
|
```
|
|
94
141
|
|
|
142
|
+
This will:
|
|
143
|
+
|
|
144
|
+
1. Find all codeowners for the staged files in your repository
|
|
145
|
+
2. Apply any ignore/include filters if specified
|
|
146
|
+
3. For each codeowner (e.g., @team-a, @team-b):
|
|
147
|
+
- Create a branch like `feature/new-feature/team-a`
|
|
148
|
+
- Commit only the files owned by that team
|
|
149
|
+
- Add a commit message like "Add new feature - @team-a"
|
|
150
|
+
- Push each branch to the remote if the `-p` flag is provided
|
|
151
|
+
|
|
95
152
|
## Contributing
|
|
96
153
|
|
|
97
154
|
1. Clone the repository
|
|
@@ -111,6 +168,7 @@ bun test
|
|
|
111
168
|
5. Submit a pull request
|
|
112
169
|
|
|
113
170
|
## Alternatives
|
|
171
|
+
|
|
114
172
|
[@snyk/github-codeowners](https://github.com/snyk/github-codeowners)
|
|
115
173
|
|
|
116
174
|
[codeowners](https://github.com/beaugunderson/codeowners)
|
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
|
|
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
|
|
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)}`),
|
|
@@ -14600,38 +14582,128 @@ ${message}`)),
|
|
|
14600
14582
|
};
|
|
14601
14583
|
|
|
14602
14584
|
// src/utils/git.ts
|
|
14585
|
+
import { spawn as spawn2 } from "child_process";
|
|
14603
14586
|
var git = esm_default();
|
|
14604
14587
|
var getChangedFiles = async () => {
|
|
14605
14588
|
const status = await git.status();
|
|
14606
14589
|
return status.files.map((file) => file.path);
|
|
14607
14590
|
};
|
|
14591
|
+
var branchExists = async (branchName) => {
|
|
14592
|
+
try {
|
|
14593
|
+
const branches = await git.branch();
|
|
14594
|
+
return branches.all.includes(branchName);
|
|
14595
|
+
} catch (error) {
|
|
14596
|
+
log.error(`Failed to check if branch exists: ${error}`);
|
|
14597
|
+
return false;
|
|
14598
|
+
}
|
|
14599
|
+
};
|
|
14608
14600
|
var createBranch = async (branchName) => {
|
|
14609
14601
|
log.info(`Switching to a new local branch: "${branchName}"`);
|
|
14610
|
-
|
|
14611
|
-
|
|
14602
|
+
try {
|
|
14603
|
+
await git.checkoutLocalBranch(branchName);
|
|
14604
|
+
log.info(`Now on branch: "${branchName}"`);
|
|
14605
|
+
} catch (error) {
|
|
14606
|
+
throw new Error(`Failed to create branch "${branchName}": ${error}`);
|
|
14607
|
+
}
|
|
14608
|
+
};
|
|
14609
|
+
var deleteBranch = async (branchName, force = false) => {
|
|
14610
|
+
try {
|
|
14611
|
+
if (await branchExists(branchName)) {
|
|
14612
|
+
log.info(`Deleting branch: "${branchName}"${force ? " (forced)" : ""}`);
|
|
14613
|
+
await git.branch([force ? "-D" : "-d", branchName]);
|
|
14614
|
+
log.info(`Branch "${branchName}" deleted successfully`);
|
|
14615
|
+
return true;
|
|
14616
|
+
}
|
|
14617
|
+
return false;
|
|
14618
|
+
} catch (error) {
|
|
14619
|
+
log.error(`Failed to delete branch "${branchName}": ${error}`);
|
|
14620
|
+
return false;
|
|
14621
|
+
}
|
|
14612
14622
|
};
|
|
14613
14623
|
var checkout = async (name) => {
|
|
14614
14624
|
log.info(`Switching to branch: "${name}"`);
|
|
14615
|
-
|
|
14625
|
+
try {
|
|
14626
|
+
await git.checkout(name);
|
|
14627
|
+
} catch (error) {
|
|
14628
|
+
throw new Error(`Failed to checkout branch "${name}": ${error}`);
|
|
14629
|
+
}
|
|
14616
14630
|
};
|
|
14617
14631
|
var commitChanges = async (files, { message, noVerify = false }) => {
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
|
|
14623
|
-
|
|
14624
|
-
|
|
14625
|
-
|
|
14632
|
+
try {
|
|
14633
|
+
log.info("Adding files to commit...");
|
|
14634
|
+
await git.add(files);
|
|
14635
|
+
log.info(`Running commit with message: "${message}"`);
|
|
14636
|
+
await git.commit(message, [], {
|
|
14637
|
+
...noVerify ? { "--no-verify": null } : {}
|
|
14638
|
+
});
|
|
14639
|
+
log.info("Commit finished successfully.");
|
|
14640
|
+
} catch (error) {
|
|
14641
|
+
log.error(`Failed to commit changes: ${error}`);
|
|
14642
|
+
throw new Error(`Commit failed: ${error}`);
|
|
14643
|
+
}
|
|
14626
14644
|
};
|
|
14627
14645
|
var getCurrentBranch = async () => {
|
|
14628
|
-
|
|
14646
|
+
try {
|
|
14647
|
+
return await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
14648
|
+
} catch (error) {
|
|
14649
|
+
throw new Error(`Failed to get current branch: ${error}`);
|
|
14650
|
+
}
|
|
14651
|
+
};
|
|
14652
|
+
var pushBranch = async (branchName, {
|
|
14653
|
+
remote = "origin",
|
|
14654
|
+
upstream,
|
|
14655
|
+
force = false,
|
|
14656
|
+
noVerify = false
|
|
14657
|
+
} = {}) => {
|
|
14658
|
+
const targetUpstream = upstream || branchName;
|
|
14659
|
+
log.info(`Pushing branch "${branchName}" to ${remote}/${targetUpstream}...`);
|
|
14660
|
+
try {
|
|
14661
|
+
const pushArgs = [];
|
|
14662
|
+
pushArgs.push(remote, `${branchName}:${targetUpstream}`);
|
|
14663
|
+
if (force) {
|
|
14664
|
+
pushArgs.push("--force");
|
|
14665
|
+
}
|
|
14666
|
+
if (noVerify) {
|
|
14667
|
+
pushArgs.push("--no-verify");
|
|
14668
|
+
}
|
|
14669
|
+
const gitProcess = spawn2("git", ["push", ...pushArgs], {
|
|
14670
|
+
stdio: ["inherit", "inherit", "inherit"]
|
|
14671
|
+
});
|
|
14672
|
+
return new Promise((resolve, reject) => {
|
|
14673
|
+
gitProcess.on("close", (code) => {
|
|
14674
|
+
if (code === 0) {
|
|
14675
|
+
log.success(`Successfully pushed to ${remote}/${targetUpstream}`);
|
|
14676
|
+
resolve();
|
|
14677
|
+
} else {
|
|
14678
|
+
const error = new Error(`Push failed with exit code ${code}`);
|
|
14679
|
+
log.error(`Failed to push to remote: ${error}`);
|
|
14680
|
+
reject(error);
|
|
14681
|
+
}
|
|
14682
|
+
});
|
|
14683
|
+
});
|
|
14684
|
+
} catch (error) {
|
|
14685
|
+
log.error(`Failed to push to remote: ${error}`);
|
|
14686
|
+
throw new Error(`Push failed: ${error}`);
|
|
14687
|
+
}
|
|
14629
14688
|
};
|
|
14630
14689
|
|
|
14631
14690
|
// src/utils/codeowners.ts
|
|
14632
|
-
var codeowners
|
|
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
|
+
};
|
|
14633
14704
|
var getOwner = (filePath) => {
|
|
14634
|
-
const
|
|
14705
|
+
const instance = getCodeownersInstance();
|
|
14706
|
+
const owner = instance.getOwner(filePath);
|
|
14635
14707
|
return owner;
|
|
14636
14708
|
};
|
|
14637
14709
|
var getOwnerFiles = async (owner) => {
|
|
@@ -14703,35 +14775,173 @@ var listCodeowners = async (options) => {
|
|
|
14703
14775
|
|
|
14704
14776
|
// src/commands/branch.ts
|
|
14705
14777
|
var branch = async (options) => {
|
|
14778
|
+
let originalBranch = "";
|
|
14779
|
+
let stashId = null;
|
|
14780
|
+
let newBranchCreated = false;
|
|
14781
|
+
let filesToCommit = [];
|
|
14706
14782
|
try {
|
|
14707
14783
|
if (!options.branch || !options.message || !options.owner) {
|
|
14708
14784
|
throw new Error("Missing required options for branch creation");
|
|
14709
14785
|
}
|
|
14710
|
-
const filesToCommit = await getOwnerFiles(options.owner);
|
|
14711
|
-
if (filesToCommit.length <= 0) {
|
|
14712
|
-
throw new Error(`No files found for ${options.owner}`);
|
|
14713
|
-
}
|
|
14714
14786
|
log.info("Starting branch creation process...");
|
|
14715
|
-
|
|
14787
|
+
originalBranch = await getCurrentBranch();
|
|
14716
14788
|
log.info(`Currently on branch: ${originalBranch}`);
|
|
14789
|
+
filesToCommit = await getOwnerFiles(options.owner);
|
|
14790
|
+
if (filesToCommit.length <= 0) {
|
|
14791
|
+
log.warn(`No files found for ${options.owner}. Skipping branch creation.`);
|
|
14792
|
+
return;
|
|
14793
|
+
}
|
|
14717
14794
|
log.file(`Files to be committed:
|
|
14718
14795
|
${filesToCommit.join(`
|
|
14719
14796
|
`)}`);
|
|
14797
|
+
if (await branchExists(options.branch)) {
|
|
14798
|
+
throw new Error(`Branch "${options.branch}" already exists. Use a different name or delete the existing branch first.`);
|
|
14799
|
+
}
|
|
14720
14800
|
try {
|
|
14721
14801
|
log.info(`Creating new branch "${options.branch}"...`);
|
|
14722
14802
|
await createBranch(options.branch);
|
|
14723
|
-
|
|
14803
|
+
newBranchCreated = true;
|
|
14804
|
+
log.info(`Committing changes with message: "${options.message}" ${!options.verify ? "(no-verify)" : ""}...`);
|
|
14724
14805
|
await commitChanges(filesToCommit, {
|
|
14725
14806
|
message: options.message ?? "",
|
|
14726
14807
|
noVerify: !options.verify
|
|
14727
14808
|
});
|
|
14728
|
-
|
|
14809
|
+
if (options.push) {
|
|
14810
|
+
await pushBranch(options.branch, {
|
|
14811
|
+
remote: options.remote,
|
|
14812
|
+
upstream: options.upstream,
|
|
14813
|
+
force: options.force,
|
|
14814
|
+
noVerify: !options.verify
|
|
14815
|
+
});
|
|
14816
|
+
}
|
|
14729
14817
|
log.info(`Checking out original branch "${originalBranch}"...`);
|
|
14730
14818
|
await checkout(originalBranch);
|
|
14731
|
-
log.success(`Branch "${options.branch}" created and changes committed.`);
|
|
14819
|
+
log.success(options.push ? `Branch "${options.branch}" created, changes committed, and pushed to remote.` : `Branch "${options.branch}" created and changes committed.`);
|
|
14820
|
+
} catch (operationError) {
|
|
14821
|
+
log.error(`Operation failed: ${operationError}`);
|
|
14822
|
+
if (newBranchCreated) {
|
|
14823
|
+
try {
|
|
14824
|
+
log.info(`Returning to original branch "${originalBranch}"...`);
|
|
14825
|
+
await checkout(originalBranch);
|
|
14826
|
+
if (!options.keepBranchOnFailure) {
|
|
14827
|
+
log.info(`Cleaning up: Deleting branch "${options.branch}"...`);
|
|
14828
|
+
await deleteBranch(options.branch, true);
|
|
14829
|
+
} else {
|
|
14830
|
+
log.info(`Branch "${options.branch}" was kept despite the failure.`);
|
|
14831
|
+
}
|
|
14832
|
+
} catch (cleanupError) {
|
|
14833
|
+
log.error(`Error during cleanup: ${cleanupError}`);
|
|
14834
|
+
}
|
|
14835
|
+
}
|
|
14836
|
+
throw operationError;
|
|
14732
14837
|
}
|
|
14733
14838
|
} catch (err) {
|
|
14734
|
-
log.error(err);
|
|
14839
|
+
log.error(`Branch operation failed: ${err}`);
|
|
14840
|
+
process.exit(1);
|
|
14841
|
+
} finally {
|
|
14842
|
+
try {
|
|
14843
|
+
if (originalBranch) {
|
|
14844
|
+
const currentBranch = await getCurrentBranch();
|
|
14845
|
+
if (currentBranch !== originalBranch) {
|
|
14846
|
+
await checkout(originalBranch);
|
|
14847
|
+
}
|
|
14848
|
+
}
|
|
14849
|
+
} catch (finalError) {
|
|
14850
|
+
log.error(`Error during final cleanup: ${finalError}`);
|
|
14851
|
+
log.info("Some manual cleanup may be required.");
|
|
14852
|
+
}
|
|
14853
|
+
}
|
|
14854
|
+
};
|
|
14855
|
+
|
|
14856
|
+
// src/commands/multi-branch.ts
|
|
14857
|
+
var import_micromatch2 = __toESM(require_micromatch(), 1);
|
|
14858
|
+
var multiBranch = async (options) => {
|
|
14859
|
+
try {
|
|
14860
|
+
if (!options.branch || !options.message) {
|
|
14861
|
+
throw new Error("Missing required options for multi-branch creation");
|
|
14862
|
+
}
|
|
14863
|
+
if (options.ignore && options.include) {
|
|
14864
|
+
throw new Error("Cannot use both --ignore and --include options at the same time");
|
|
14865
|
+
}
|
|
14866
|
+
log.info("Starting multi-branch creation process...");
|
|
14867
|
+
const changedFiles = await getChangedFiles();
|
|
14868
|
+
if (changedFiles.length === 0) {
|
|
14869
|
+
throw new Error("No changed files found in the repository");
|
|
14870
|
+
}
|
|
14871
|
+
const ownerSet = new Set;
|
|
14872
|
+
for (const file of changedFiles) {
|
|
14873
|
+
const owners = getOwner(file);
|
|
14874
|
+
for (const owner of owners) {
|
|
14875
|
+
ownerSet.add(owner);
|
|
14876
|
+
}
|
|
14877
|
+
}
|
|
14878
|
+
let codeowners2 = Array.from(ownerSet);
|
|
14879
|
+
if (codeowners2.length === 0) {
|
|
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(", ")}`);
|
|
14907
|
+
}
|
|
14908
|
+
const results = {
|
|
14909
|
+
success: [],
|
|
14910
|
+
failure: []
|
|
14911
|
+
};
|
|
14912
|
+
for (const owner of codeowners2) {
|
|
14913
|
+
try {
|
|
14914
|
+
const sanitizedOwner = owner.replace(/[^a-zA-Z0-9-_@]/g, "-").replace(/^@/, "");
|
|
14915
|
+
const branchName = `${options.branch}/${sanitizedOwner}`;
|
|
14916
|
+
const commitMessage = `${options.message} - ${owner}`;
|
|
14917
|
+
log.info(`Creating branch for ${owner}...`);
|
|
14918
|
+
await branch({
|
|
14919
|
+
owner,
|
|
14920
|
+
branch: branchName,
|
|
14921
|
+
message: commitMessage,
|
|
14922
|
+
verify: options.verify,
|
|
14923
|
+
push: options.push,
|
|
14924
|
+
remote: options.remote,
|
|
14925
|
+
upstream: options.upstream,
|
|
14926
|
+
force: options.force,
|
|
14927
|
+
keepBranchOnFailure: options.keepBranchOnFailure
|
|
14928
|
+
});
|
|
14929
|
+
results.success.push(owner);
|
|
14930
|
+
} catch (error) {
|
|
14931
|
+
log.error(`Failed to create branch for ${owner}: ${error}`);
|
|
14932
|
+
results.failure.push(owner);
|
|
14933
|
+
}
|
|
14934
|
+
}
|
|
14935
|
+
log.header("Multi-branch creation summary");
|
|
14936
|
+
log.info(`Successfully created branches for ${results.success.length} of ${codeowners2.length} codeowners`);
|
|
14937
|
+
if (results.success.length) {
|
|
14938
|
+
log.success(`Successful: ${results.success.join(", ")}`);
|
|
14939
|
+
}
|
|
14940
|
+
if (results.failure.length) {
|
|
14941
|
+
log.error(`Failed: ${results.failure.join(", ")}`);
|
|
14942
|
+
}
|
|
14943
|
+
} catch (err) {
|
|
14944
|
+
log.error(`Multi-branch operation failed: ${err}`);
|
|
14735
14945
|
process.exit(1);
|
|
14736
14946
|
}
|
|
14737
14947
|
};
|
|
@@ -14740,5 +14950,6 @@ var branch = async (options) => {
|
|
|
14740
14950
|
var program2 = new Command;
|
|
14741
14951
|
program2.name("codeowners").description("CLI tool for grouping and managing staged files by CODEOWNERS");
|
|
14742
14952
|
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);
|
|
14743
|
-
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").action(branch);
|
|
14953
|
+
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);
|
|
14954
|
+
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);
|
|
14744
14955
|
program2.parse(process.argv);
|