@biffo/cli 0.199.2 → 0.199.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/dist/index.js +132 -58
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1222,16 +1222,37 @@ function formatBranchProtectionSummary(outcomes) {
|
|
|
1222
1222
|
];
|
|
1223
1223
|
for (const outcome of unprotected) {
|
|
1224
1224
|
const left = outcome.unprotectedBranches.join(", ") || "unknown";
|
|
1225
|
-
|
|
1226
|
-
|
|
1225
|
+
lines.push(
|
|
1226
|
+
` ${outcome.org}/${outcome.repo} \u2014 unprotected: ${left} (${whyUnprotected(outcome)})`
|
|
1227
|
+
);
|
|
1227
1228
|
if (outcome.reason) lines.push(` ${outcome.reason}`);
|
|
1228
1229
|
}
|
|
1229
1230
|
lines.push(
|
|
1230
|
-
" Direct pushes, force-pushes and merges with red or missing checks are all allowed on those branches right now."
|
|
1231
|
-
|
|
1231
|
+
" Direct pushes, force-pushes and merges with red or missing checks are all allowed on those branches right now."
|
|
1232
|
+
);
|
|
1233
|
+
if (unprotected.some((o) => o.status === "skipped-no-contexts")) {
|
|
1234
|
+
lines.push(
|
|
1235
|
+
" Where the required checks could not be determined, the fix is the CI job names, not the plan: make the workflow report named jobs, let one run complete, then backfill."
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
const has403 = unprotected.some((o) => o.status === "skipped-403");
|
|
1239
|
+
lines.push(
|
|
1240
|
+
" Fix it with: biffo check branch-protection --fix " + (has403 ? "(after upgrading the plan, or making the repo public)" : "(it derives the required checks from the runs the repo has actually reported)")
|
|
1232
1241
|
);
|
|
1233
1242
|
return lines;
|
|
1234
1243
|
}
|
|
1244
|
+
function whyUnprotected(outcome) {
|
|
1245
|
+
switch (outcome.status) {
|
|
1246
|
+
case "skipped-403":
|
|
1247
|
+
return "GitHub returned 403 \u2014 the org's plan does not allow branch protection on this repo";
|
|
1248
|
+
case "skipped-no-contexts":
|
|
1249
|
+
return "the required status checks could not be determined, and protection requiring nothing reads as configured while gating on nothing";
|
|
1250
|
+
case "failed":
|
|
1251
|
+
return "branch protection failed";
|
|
1252
|
+
default:
|
|
1253
|
+
return "branch protection incomplete";
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1235
1256
|
function reportBranchProtectionSummary() {
|
|
1236
1257
|
const outcomes = pending.splice(0, pending.length);
|
|
1237
1258
|
const lines = formatBranchProtectionSummary(outcomes);
|
|
@@ -1754,51 +1775,91 @@ var GitHubAdapter = class {
|
|
|
1754
1775
|
* `statusChecks` must be non-empty. Protection that requires nothing is worse
|
|
1755
1776
|
* than none: it reads as configured while gating on nothing, so callers that
|
|
1756
1777
|
* could not determine the contexts must not silently land here.
|
|
1778
|
+
*
|
|
1779
|
+
* Like `configureBranchProtection`, this **returns and records** what actually
|
|
1780
|
+
* happened (#1001). It used to return `Promise<void>` and swallow the 403 with
|
|
1781
|
+
* two log warnings, so `biffo plugin create --org` printed its next-steps
|
|
1782
|
+
* block and exited 0 with nothing anywhere recording that the repo it had just
|
|
1783
|
+
* created had an unprotected `dev` — the identical defect #715 described for
|
|
1784
|
+
* the deployable path, on the path #999 deliberately left out of scope.
|
|
1757
1785
|
*/
|
|
1758
1786
|
async protectSingleBranch(org, repo, branch, statusChecks, protectionIntervalMs = 3e3) {
|
|
1759
1787
|
if (statusChecks.length === 0) {
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1788
|
+
const message = `Refusing to protect ${org}/${repo}@${branch} with no required status checks \u2014 that reads as protected while gating on nothing. Determine the workflow's job contexts first.`;
|
|
1789
|
+
recordBranchProtectionOutcome({
|
|
1790
|
+
status: "skipped-no-contexts",
|
|
1791
|
+
org,
|
|
1792
|
+
repo,
|
|
1793
|
+
protectedBranches: [],
|
|
1794
|
+
unprotectedBranches: [branch],
|
|
1795
|
+
reason: message
|
|
1796
|
+
});
|
|
1797
|
+
throw new Error(message);
|
|
1763
1798
|
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1799
|
+
try {
|
|
1800
|
+
log.info(`Waiting for ${branch} branch to be ready...`);
|
|
1801
|
+
await this.waitForBranch(org, repo, branch);
|
|
1802
|
+
log.info(`Configuring branch protection on ${branch}...`);
|
|
1803
|
+
const params = {
|
|
1804
|
+
owner: org,
|
|
1805
|
+
repo,
|
|
1806
|
+
branch,
|
|
1807
|
+
required_status_checks: { strict: true, contexts: statusChecks },
|
|
1808
|
+
enforce_admins: false,
|
|
1809
|
+
required_pull_request_reviews: {
|
|
1810
|
+
required_approving_review_count: 0,
|
|
1811
|
+
dismiss_stale_reviews: false
|
|
1812
|
+
},
|
|
1813
|
+
restrictions: null,
|
|
1814
|
+
required_linear_history: true,
|
|
1815
|
+
allow_force_pushes: false,
|
|
1816
|
+
allow_deletions: false
|
|
1817
|
+
};
|
|
1818
|
+
const deadline = Date.now() + 3e4;
|
|
1819
|
+
while (true) {
|
|
1820
|
+
try {
|
|
1821
|
+
await this.octokit.repos.updateBranchProtection(params);
|
|
1822
|
+
break;
|
|
1823
|
+
} catch (err) {
|
|
1824
|
+
const status = err.status;
|
|
1825
|
+
if (status === 403) {
|
|
1826
|
+
log.warn(`Branch protection unavailable for ${org}/${repo}: ${err.message}`);
|
|
1827
|
+
log.warn(" Add it later via GitHub once the plan allows it, or make the repo public.");
|
|
1828
|
+
return recordBranchProtectionOutcome({
|
|
1829
|
+
status: "skipped-403",
|
|
1830
|
+
org,
|
|
1831
|
+
repo,
|
|
1832
|
+
protectedBranches: [],
|
|
1833
|
+
unprotectedBranches: [branch],
|
|
1834
|
+
reason: err.message
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
if (status !== 404 || Date.now() >= deadline) throw err;
|
|
1838
|
+
log.info("Branch protection endpoint not yet ready, retrying...");
|
|
1839
|
+
await new Promise((resolve19) => setTimeout(resolve19, protectionIntervalMs));
|
|
1793
1840
|
}
|
|
1794
|
-
if (status !== 404 || Date.now() >= deadline) throw err;
|
|
1795
|
-
log.info("Branch protection endpoint not yet ready, retrying...");
|
|
1796
|
-
await new Promise((resolve19) => setTimeout(resolve19, protectionIntervalMs));
|
|
1797
1841
|
}
|
|
1842
|
+
} catch (err) {
|
|
1843
|
+
recordBranchProtectionOutcome({
|
|
1844
|
+
status: "failed",
|
|
1845
|
+
org,
|
|
1846
|
+
repo,
|
|
1847
|
+
protectedBranches: [],
|
|
1848
|
+
unprotectedBranches: [branch],
|
|
1849
|
+
reason: err.message
|
|
1850
|
+
});
|
|
1851
|
+
throw err;
|
|
1798
1852
|
}
|
|
1799
1853
|
log.success(
|
|
1800
1854
|
`Branch protection configured on ${branch} (${statusChecks.length} required check(s))`
|
|
1801
1855
|
);
|
|
1856
|
+
return recordBranchProtectionOutcome({
|
|
1857
|
+
status: "applied",
|
|
1858
|
+
org,
|
|
1859
|
+
repo,
|
|
1860
|
+
protectedBranches: [branch],
|
|
1861
|
+
unprotectedBranches: []
|
|
1862
|
+
});
|
|
1802
1863
|
}
|
|
1803
1864
|
async createEnvironments(config) {
|
|
1804
1865
|
const { org, repo } = config.source_control.config;
|
|
@@ -7182,25 +7243,29 @@ var pluginCreateCommand = new Command13("create").description("Scaffold a new pl
|
|
|
7182
7243
|
async (name, options) => {
|
|
7183
7244
|
const cwd = options.cwd ? resolve11(options.cwd) : process.cwd();
|
|
7184
7245
|
try {
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7246
|
+
try {
|
|
7247
|
+
await runPluginCreate(
|
|
7248
|
+
name,
|
|
7249
|
+
{
|
|
7250
|
+
firstParty: options.firstParty ?? false,
|
|
7251
|
+
standalone: options.standalone ?? false,
|
|
7252
|
+
...options.org ? { org: options.org } : {},
|
|
7253
|
+
...options.runnerLabel ? { runnerLabel: options.runnerLabel } : {},
|
|
7254
|
+
register: options.register !== false,
|
|
7255
|
+
...options.skeleton ? { skeletonRoot: resolve11(options.skeleton) } : {},
|
|
7256
|
+
dryRun: options.dryRun ?? false,
|
|
7257
|
+
commit: options.commit !== false,
|
|
7258
|
+
cwd
|
|
7259
|
+
},
|
|
7260
|
+
{
|
|
7261
|
+
git: new GitAdapter(),
|
|
7262
|
+
makeGitHub: (token) => new GitHubAdapter(token),
|
|
7263
|
+
resolveToken: resolveGithubToken3
|
|
7264
|
+
}
|
|
7265
|
+
);
|
|
7266
|
+
} finally {
|
|
7267
|
+
reportBranchProtectionSummary();
|
|
7268
|
+
}
|
|
7204
7269
|
} catch (err) {
|
|
7205
7270
|
log.error(err.message);
|
|
7206
7271
|
process.exit(1);
|
|
@@ -7216,6 +7281,7 @@ async function runPluginCreate(name, options, deps) {
|
|
|
7216
7281
|
);
|
|
7217
7282
|
}
|
|
7218
7283
|
await runStandaloneCreate(names, options, deps);
|
|
7284
|
+
reportBranchProtectionSummary();
|
|
7219
7285
|
return;
|
|
7220
7286
|
}
|
|
7221
7287
|
const isInstance = existsSync23(join23(options.cwd, INSTANCE_CORE_FILE));
|
|
@@ -7341,6 +7407,14 @@ async function createAndPushStandaloneRepo(org, names, destDir, options, deps) {
|
|
|
7341
7407
|
log.warn(
|
|
7342
7408
|
`Could not determine required status checks from ${ciPath} \u2014 skipping branch protection. Configure it manually on dev once you know the CI job names.`
|
|
7343
7409
|
);
|
|
7410
|
+
recordBranchProtectionOutcome({
|
|
7411
|
+
status: "skipped-no-contexts",
|
|
7412
|
+
org,
|
|
7413
|
+
repo: names.dist,
|
|
7414
|
+
protectedBranches: [],
|
|
7415
|
+
unprotectedBranches: ["dev"],
|
|
7416
|
+
reason: `No status-check contexts could be derived from ${ciPath}, so there was nothing to require. Protection with an empty context list reads as configured while admitting every PR, so none was applied.`
|
|
7417
|
+
});
|
|
7344
7418
|
} else {
|
|
7345
7419
|
await github.protectSingleBranch(org, names.dist, "dev", contexts);
|
|
7346
7420
|
}
|