split-by-codeowners 1.0.0 → 1.0.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/action.yml +10 -0
- package/dist/index.js +133 -11
- package/dist/index.js.map +1 -1
- package/dist-cli/index.js +141 -11
- package/dist-cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist-cli/index.js
CHANGED
|
@@ -1829,6 +1829,7 @@ const codeowners_1 = __nccwpck_require__(586);
|
|
|
1829
1829
|
const git_1 = __nccwpck_require__(243);
|
|
1830
1830
|
const github_1 = __nccwpck_require__(248);
|
|
1831
1831
|
const ghcli_1 = __nccwpck_require__(64);
|
|
1832
|
+
const pr_template_1 = __nccwpck_require__(456);
|
|
1832
1833
|
function formatTemplate(tpl, vars) {
|
|
1833
1834
|
let out = tpl;
|
|
1834
1835
|
for (const [k, v] of Object.entries(vars))
|
|
@@ -1948,8 +1949,22 @@ async function runSplit(config, logger) {
|
|
|
1948
1949
|
(0, git_1.pushBranch)(config.remoteName, branch, worktreeDir);
|
|
1949
1950
|
const ownersStr = b.owners.length ? b.owners.join(", ") : "(unowned)";
|
|
1950
1951
|
const filesStr = b.files.map(f => `- ${f.file}`).join("\n");
|
|
1952
|
+
const bucketInfo = formatTemplate("Automated changes bucketed by CODEOWNERS.\n\nOwners: {owners}\nBucket key: {bucket_key}\n\nFiles:\n{files}\n", { owners: ownersStr, bucket_key: b.key, files: filesStr });
|
|
1951
1953
|
const title = formatTemplate(config.prTitle, { owners: ownersStr, bucket_key: b.key });
|
|
1952
|
-
|
|
1954
|
+
let body;
|
|
1955
|
+
if (config.prBodyMode === "none") {
|
|
1956
|
+
body = undefined;
|
|
1957
|
+
}
|
|
1958
|
+
else if (config.prBodyMode === "custom") {
|
|
1959
|
+
body = formatTemplate(config.prBody, { owners: ownersStr, bucket_key: b.key, files: filesStr });
|
|
1960
|
+
}
|
|
1961
|
+
else {
|
|
1962
|
+
const template = (0, pr_template_1.readPrTemplate)(worktreeDir, config.prTemplatePath) ?? "";
|
|
1963
|
+
body =
|
|
1964
|
+
config.prBodyMode === "template_with_bucket"
|
|
1965
|
+
? (template ? template.trimEnd() + "\n\n---\n\n" + bucketInfo : bucketInfo)
|
|
1966
|
+
: (template || bucketInfo);
|
|
1967
|
+
}
|
|
1953
1968
|
const pr = useGhCli
|
|
1954
1969
|
? (() => {
|
|
1955
1970
|
return (0, ghcli_1.upsertPullRequestViaGh)({
|
|
@@ -1957,7 +1972,7 @@ async function runSplit(config, logger) {
|
|
|
1957
1972
|
base: baseBranch,
|
|
1958
1973
|
head: branch,
|
|
1959
1974
|
title,
|
|
1960
|
-
body,
|
|
1975
|
+
body: body ?? "",
|
|
1961
1976
|
draft: config.draft,
|
|
1962
1977
|
bucketKey: b.key
|
|
1963
1978
|
});
|
|
@@ -1968,7 +1983,7 @@ async function runSplit(config, logger) {
|
|
|
1968
1983
|
base: baseBranch,
|
|
1969
1984
|
head: branch,
|
|
1970
1985
|
title,
|
|
1971
|
-
body,
|
|
1986
|
+
body: body ?? "",
|
|
1972
1987
|
draft: config.draft,
|
|
1973
1988
|
bucketKey: b.key
|
|
1974
1989
|
});
|
|
@@ -2073,6 +2088,35 @@ exports.parseCodeowners = parseCodeowners;
|
|
|
2073
2088
|
exports.ownersForFile = ownersForFile;
|
|
2074
2089
|
const node_fs_1 = __importDefault(__nccwpck_require__(24));
|
|
2075
2090
|
const minimatch_1 = __nccwpck_require__(507);
|
|
2091
|
+
function splitPatternOwners(line) {
|
|
2092
|
+
// CODEOWNERS lines are: <pattern><whitespace><owner>...
|
|
2093
|
+
// Patterns may contain spaces if escaped as `\ `.
|
|
2094
|
+
// We parse by finding the first whitespace not escaped by a backslash.
|
|
2095
|
+
let i = 0;
|
|
2096
|
+
let escaped = false;
|
|
2097
|
+
for (; i < line.length; i++) {
|
|
2098
|
+
const ch = line[i];
|
|
2099
|
+
if (escaped) {
|
|
2100
|
+
escaped = false;
|
|
2101
|
+
continue;
|
|
2102
|
+
}
|
|
2103
|
+
if (ch === "\\") {
|
|
2104
|
+
escaped = true;
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
if (/\s/.test(ch))
|
|
2108
|
+
break;
|
|
2109
|
+
}
|
|
2110
|
+
const rawPattern = line.slice(0, i).trim();
|
|
2111
|
+
const rest = line.slice(i).trim();
|
|
2112
|
+
if (!rawPattern || !rest)
|
|
2113
|
+
return null;
|
|
2114
|
+
const pattern = rawPattern.replaceAll("\\ ", " ");
|
|
2115
|
+
const owners = rest.split(/\s+/).filter(Boolean);
|
|
2116
|
+
if (!owners.length)
|
|
2117
|
+
return null;
|
|
2118
|
+
return { pattern, owners };
|
|
2119
|
+
}
|
|
2076
2120
|
function parseCodeowners(path) {
|
|
2077
2121
|
const text = node_fs_1.default.readFileSync(path, "utf8");
|
|
2078
2122
|
const rules = [];
|
|
@@ -2080,18 +2124,60 @@ function parseCodeowners(path) {
|
|
|
2080
2124
|
const line = raw.trim();
|
|
2081
2125
|
if (!line || line.startsWith("#"))
|
|
2082
2126
|
continue;
|
|
2083
|
-
const
|
|
2084
|
-
if (
|
|
2127
|
+
const parsed = splitPatternOwners(line);
|
|
2128
|
+
if (!parsed)
|
|
2085
2129
|
continue;
|
|
2086
|
-
rules.push({ pattern:
|
|
2130
|
+
rules.push({ pattern: parsed.pattern, owners: parsed.owners });
|
|
2087
2131
|
}
|
|
2088
2132
|
return rules;
|
|
2089
2133
|
}
|
|
2134
|
+
function hasGlobMagic(p) {
|
|
2135
|
+
// Good-enough heuristic for gitignore-style globs.
|
|
2136
|
+
return /[*?\[]/.test(p);
|
|
2137
|
+
}
|
|
2090
2138
|
function matches(file, pattern) {
|
|
2091
|
-
|
|
2092
|
-
//
|
|
2093
|
-
|
|
2094
|
-
|
|
2139
|
+
// GitHub CODEOWNERS patterns are gitignore-like:
|
|
2140
|
+
// - Leading `/` anchors to repo root
|
|
2141
|
+
// - Pattern without `/` matches a basename anywhere
|
|
2142
|
+
// - A directory pattern matches everything under it (e.g. `/frontend/admin` should match `/frontend/admin/**`)
|
|
2143
|
+
const f = file.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
2144
|
+
let pat = pattern.replace(/\\/g, "/").trim();
|
|
2145
|
+
if (!pat)
|
|
2146
|
+
return false;
|
|
2147
|
+
const anchored = pat.startsWith("/");
|
|
2148
|
+
if (anchored)
|
|
2149
|
+
pat = pat.slice(1);
|
|
2150
|
+
// Directory patterns: trailing `/` means directory; also treat non-glob literal paths as directory-or-file match.
|
|
2151
|
+
const trailingSlash = pat.endsWith("/");
|
|
2152
|
+
if (trailingSlash)
|
|
2153
|
+
pat = pat.slice(0, -1);
|
|
2154
|
+
const isLiteral = !hasGlobMagic(pat);
|
|
2155
|
+
const hasSlash = pat.includes("/");
|
|
2156
|
+
// Literal path: match exact file OR directory subtree.
|
|
2157
|
+
if (isLiteral && (anchored || hasSlash)) {
|
|
2158
|
+
return f === pat || f.startsWith(pat + "/");
|
|
2159
|
+
}
|
|
2160
|
+
// Literal basename: match basename OR directory subtree anywhere.
|
|
2161
|
+
if (isLiteral && !hasSlash) {
|
|
2162
|
+
const base = f.split("/").pop() ?? f;
|
|
2163
|
+
if (base === pat)
|
|
2164
|
+
return true;
|
|
2165
|
+
return f.includes("/" + pat + "/");
|
|
2166
|
+
}
|
|
2167
|
+
// Glob patterns: use minimatch.
|
|
2168
|
+
// - If anchored: match from repo root.
|
|
2169
|
+
// - If not anchored and no slash: match basename anywhere.
|
|
2170
|
+
// - If not anchored but includes a slash: match relative to repo root (like a root CODEOWNERS / .gitignore).
|
|
2171
|
+
const matchBase = !anchored && !hasSlash;
|
|
2172
|
+
const mm = (0, minimatch_1.minimatch)(f, pat, { dot: true, matchBase });
|
|
2173
|
+
if (mm)
|
|
2174
|
+
return true;
|
|
2175
|
+
// If the pattern explicitly denotes a directory (trailing slash), also match directory subtree.
|
|
2176
|
+
if (trailingSlash) {
|
|
2177
|
+
// Convert `dir/` to subtree match.
|
|
2178
|
+
return (0, minimatch_1.minimatch)(f, pat + "/**", { dot: true, matchBase: false });
|
|
2179
|
+
}
|
|
2180
|
+
return false;
|
|
2095
2181
|
}
|
|
2096
2182
|
function ownersForFile(file, rules) {
|
|
2097
2183
|
let hit;
|
|
@@ -2398,13 +2484,47 @@ async function upsertPullRequest(params) {
|
|
|
2398
2484
|
head,
|
|
2399
2485
|
base,
|
|
2400
2486
|
title,
|
|
2401
|
-
body,
|
|
2487
|
+
body: body || "",
|
|
2402
2488
|
draft
|
|
2403
2489
|
});
|
|
2404
2490
|
return { bucket_key: bucketKey, branch: head, number: created.data.number, url: created.data.html_url };
|
|
2405
2491
|
}
|
|
2406
2492
|
|
|
2407
2493
|
|
|
2494
|
+
/***/ }),
|
|
2495
|
+
|
|
2496
|
+
/***/ 456:
|
|
2497
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
2498
|
+
|
|
2499
|
+
"use strict";
|
|
2500
|
+
|
|
2501
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
2502
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
2503
|
+
};
|
|
2504
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2505
|
+
exports.readPrTemplate = readPrTemplate;
|
|
2506
|
+
const node_fs_1 = __importDefault(__nccwpck_require__(24));
|
|
2507
|
+
const node_path_1 = __importDefault(__nccwpck_require__(760));
|
|
2508
|
+
function readPrTemplate(cwd, templatePath) {
|
|
2509
|
+
const candidates = [];
|
|
2510
|
+
if (templatePath && templatePath.trim()) {
|
|
2511
|
+
candidates.push(templatePath.trim());
|
|
2512
|
+
}
|
|
2513
|
+
else {
|
|
2514
|
+
candidates.push(".github/pull_request_template.md");
|
|
2515
|
+
candidates.push(".github/PULL_REQUEST_TEMPLATE.md");
|
|
2516
|
+
candidates.push("pull_request_template.md");
|
|
2517
|
+
}
|
|
2518
|
+
for (const rel of candidates) {
|
|
2519
|
+
const abs = node_path_1.default.resolve(cwd, rel);
|
|
2520
|
+
if (node_fs_1.default.existsSync(abs) && node_fs_1.default.statSync(abs).isFile()) {
|
|
2521
|
+
return node_fs_1.default.readFileSync(abs, "utf8");
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
return null;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
|
|
2408
2528
|
/***/ }),
|
|
2409
2529
|
|
|
2410
2530
|
/***/ 421:
|
|
@@ -13236,6 +13356,8 @@ function printHelp() {
|
|
|
13236
13356
|
" --commit-message <msg> (default: chore: automated changes)",
|
|
13237
13357
|
" --pr-title <tpl> Supports {owners} and {bucket_key}",
|
|
13238
13358
|
" --pr-body <tpl> Supports {owners}, {bucket_key}, {files}",
|
|
13359
|
+
" --pr-body-mode <mode> custom|template|template_with_bucket|none (default: custom)",
|
|
13360
|
+
" --pr-template-path <path> (default: .github/pull_request_template.md)",
|
|
13239
13361
|
" --draft <true|false> (default: false)",
|
|
13240
13362
|
"",
|
|
13241
13363
|
"Examples:",
|
|
@@ -13284,6 +13406,8 @@ async function main() {
|
|
|
13284
13406
|
let commitMessage = "chore: automated changes";
|
|
13285
13407
|
let prTitle = "chore: automated changes ({owners})";
|
|
13286
13408
|
let prBody = "Automated changes bucketed by CODEOWNERS.\n\nOwners: {owners}\nBucket key: {bucket_key}\n\nFiles:\n{files}\n";
|
|
13409
|
+
let prBodyMode = "template";
|
|
13410
|
+
let prTemplatePath = ".github/pull_request_template.md";
|
|
13287
13411
|
let draft = false;
|
|
13288
13412
|
// parse args
|
|
13289
13413
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -13323,6 +13447,10 @@ async function main() {
|
|
|
13323
13447
|
prTitle = takeArg(argv, i++, a);
|
|
13324
13448
|
else if (a === "--pr-body")
|
|
13325
13449
|
prBody = takeArg(argv, i++, a);
|
|
13450
|
+
else if (a === "--pr-body-mode")
|
|
13451
|
+
prBodyMode = takeArg(argv, i++, a);
|
|
13452
|
+
else if (a === "--pr-template-path")
|
|
13453
|
+
prTemplatePath = takeArg(argv, i++, a);
|
|
13326
13454
|
else if (a === "--draft")
|
|
13327
13455
|
draft = (0, buckets_2.parseBool)(takeArg(argv, i++, a));
|
|
13328
13456
|
else if (a.startsWith("-"))
|
|
@@ -13346,6 +13474,8 @@ async function main() {
|
|
|
13346
13474
|
commitMessage,
|
|
13347
13475
|
prTitle,
|
|
13348
13476
|
prBody,
|
|
13477
|
+
prBodyMode,
|
|
13478
|
+
prTemplatePath,
|
|
13349
13479
|
draft,
|
|
13350
13480
|
remoteName: "origin"
|
|
13351
13481
|
};
|