@metasession.co/devaudit-cli 0.3.4 → 0.3.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/index.js CHANGED
@@ -56,7 +56,7 @@ function emitJsonResult(payload) {
56
56
 
57
57
  // package.json
58
58
  var package_default = {
59
- version: "0.3.4"};
59
+ version: "0.3.6"};
60
60
 
61
61
  // src/lib/version.ts
62
62
  var CLI_VERSION = package_default.version;
@@ -1234,11 +1234,12 @@ var GitHubProvider = class {
1234
1234
  }
1235
1235
  if (!res.ok) throw new Error(`GitHub REST variable create failed: HTTP ${res.status}`);
1236
1236
  }
1237
- async applyBranchProtection(cwd, branch, requiredChecks) {
1237
+ async applyBranchProtection(cwd, branch, requiredChecks, options) {
1238
+ const reviewCount = options?.requiredReviewCount ?? 0;
1238
1239
  const body = {
1239
1240
  required_status_checks: { strict: false, contexts: requiredChecks },
1240
- enforce_admins: false,
1241
- required_pull_request_reviews: { dismiss_stale_reviews: true, required_approving_review_count: 0 },
1241
+ enforce_admins: true,
1242
+ required_pull_request_reviews: { dismiss_stale_reviews: true, required_approving_review_count: reviewCount },
1242
1243
  restrictions: null
1243
1244
  };
1244
1245
  if (this.preferGhCli && await ghAvailable()) {
@@ -1731,13 +1732,21 @@ async function bootstrapNode(ctx) {
1731
1732
  await execa("npx", ["husky", "init"], { cwd: ctx.projectPath, stdio: "inherit" });
1732
1733
  return { step: "8/11 Bootstrap hook framework", status: "ok", message: ".husky/ bootstrapped" };
1733
1734
  }
1734
-
1735
- // src/install/branch-protection.ts
1736
1735
  var REQUIRED_CHECKS = [
1737
- "Compliance Validation",
1738
- "DevAudit Release Approval",
1739
- "Quality Gates"
1736
+ "Quality Gates",
1737
+ "CI Status Fallback"
1740
1738
  ];
1739
+ var MAIN_REVIEW_COUNT = 1;
1740
+ var DEVELOP_REVIEW_COUNT = 0;
1741
+ async function resolveIntegrationBranch(projectPath) {
1742
+ try {
1743
+ const raw = await promises.readFile(join(projectPath, "sdlc-config.json"), "utf-8");
1744
+ const config = JSON.parse(raw);
1745
+ return config.integration_branch ?? "develop";
1746
+ } catch {
1747
+ return "develop";
1748
+ }
1749
+ }
1741
1750
  async function configureBranchProtection(ctx, provider) {
1742
1751
  if (ctx.installMode === "developer") {
1743
1752
  return {
@@ -1757,25 +1766,34 @@ async function configureBranchProtection(ctx, provider) {
1757
1766
  };
1758
1767
  }
1759
1768
  const repo = `${meta.owner}/${meta.name}`;
1769
+ const integrationBranch = await resolveIntegrationBranch(ctx.projectPath);
1760
1770
  if (ctx.dryRun) {
1761
1771
  return {
1762
1772
  step: "9/11 Configure branch protection",
1763
1773
  status: "planned",
1764
- message: `would apply branch protection on ${repo}:${meta.defaultBranch} with checks=${JSON.stringify(REQUIRED_CHECKS)}`
1774
+ message: `would apply branch protection on ${repo}:${meta.defaultBranch} (1 review) + ${integrationBranch} (0 reviews) with checks=${JSON.stringify(REQUIRED_CHECKS)}`
1765
1775
  };
1766
1776
  }
1767
- const result = await provider.applyBranchProtection(ctx.projectPath, meta.defaultBranch, REQUIRED_CHECKS);
1768
- if (result.applied) {
1769
- return {
1770
- step: "9/11 Configure branch protection",
1771
- status: "ok",
1772
- message: `required checks on ${meta.defaultBranch}: ${REQUIRED_CHECKS.join(", ")}`
1773
- };
1777
+ const results = [];
1778
+ const mainResult = await provider.applyBranchProtection(ctx.projectPath, meta.defaultBranch, REQUIRED_CHECKS, { requiredReviewCount: MAIN_REVIEW_COUNT });
1779
+ if (mainResult.applied) {
1780
+ results.push(`${meta.defaultBranch}: ok (${MAIN_REVIEW_COUNT} review)`);
1781
+ } else {
1782
+ results.push(`${meta.defaultBranch}: FAILED \u2014 ${mainResult.message ?? "unknown"}`);
1774
1783
  }
1784
+ if (integrationBranch !== meta.defaultBranch) {
1785
+ const devResult = await provider.applyBranchProtection(ctx.projectPath, integrationBranch, REQUIRED_CHECKS, { requiredReviewCount: DEVELOP_REVIEW_COUNT });
1786
+ if (devResult.applied) {
1787
+ results.push(`${integrationBranch}: ok (${DEVELOP_REVIEW_COUNT} reviews)`);
1788
+ } else {
1789
+ results.push(`${integrationBranch}: FAILED \u2014 ${devResult.message ?? "unknown"}`);
1790
+ }
1791
+ }
1792
+ const allOk = results.every((r) => r.includes(": ok"));
1775
1793
  return {
1776
1794
  step: "9/11 Configure branch protection",
1777
- status: "warn",
1778
- message: `${result.message ?? "branch-protection apply failed"} \u2014 configure manually`
1795
+ status: allOk ? "ok" : "warn",
1796
+ message: allOk ? results.join(" | ") : `${results.join(" | ")} \u2014 configure manually`
1779
1797
  };
1780
1798
  }
1781
1799
  var ajv = new Ajv({ allErrors: true, strict: false });
@@ -2496,6 +2514,59 @@ async function syncWorkflows(ctx) {
2496
2514
  message: files.length > 0 ? `synced to .devin/workflows/` : "no workflow files found"
2497
2515
  };
2498
2516
  }
2517
+ var REQUIRED_CHECKS2 = [
2518
+ "Quality Gates",
2519
+ "CI Status Fallback"
2520
+ ];
2521
+ var MAIN_REVIEW_COUNT2 = 1;
2522
+ var DEVELOP_REVIEW_COUNT2 = 0;
2523
+ async function verifyBranchProtection(ctx) {
2524
+ const config = await readSdlcConfig(ctx.projectPath);
2525
+ const integrationBranch = config?.integration_branch ?? "develop";
2526
+ config?.release_branch ?? "main";
2527
+ let provider;
2528
+ try {
2529
+ provider = await getGitProvider(ctx.projectPath);
2530
+ } catch (err) {
2531
+ return {
2532
+ name: "Branch protection",
2533
+ filesSynced: 0,
2534
+ warning: `git provider unavailable (${err.message}) \u2014 verify manually`
2535
+ };
2536
+ }
2537
+ let meta;
2538
+ try {
2539
+ meta = await provider.getRepoMeta(ctx.projectPath);
2540
+ } catch (err) {
2541
+ return {
2542
+ name: "Branch protection",
2543
+ filesSynced: 0,
2544
+ warning: `could not resolve repo (${err.message}) \u2014 verify manually`
2545
+ };
2546
+ }
2547
+ const results = [];
2548
+ const mainResult = await provider.applyBranchProtection(ctx.projectPath, meta.defaultBranch, REQUIRED_CHECKS2, { requiredReviewCount: MAIN_REVIEW_COUNT2 });
2549
+ if (mainResult.applied) {
2550
+ results.push(`${meta.defaultBranch}: ok`);
2551
+ } else {
2552
+ results.push(`${meta.defaultBranch}: failed \u2014 ${mainResult.message ?? "unknown"}`);
2553
+ }
2554
+ if (integrationBranch !== meta.defaultBranch) {
2555
+ const devResult = await provider.applyBranchProtection(ctx.projectPath, integrationBranch, REQUIRED_CHECKS2, { requiredReviewCount: DEVELOP_REVIEW_COUNT2 });
2556
+ if (devResult.applied) {
2557
+ results.push(`${integrationBranch}: ok`);
2558
+ } else {
2559
+ results.push(`${integrationBranch}: failed \u2014 ${devResult.message ?? "unknown"}`);
2560
+ }
2561
+ }
2562
+ const allOk = results.every((r) => r.endsWith(": ok"));
2563
+ return {
2564
+ name: "Branch protection",
2565
+ filesSynced: 0,
2566
+ message: results.join(" | "),
2567
+ warning: allOk ? void 0 : "some branches failed \u2014 verify manually"
2568
+ };
2569
+ }
2499
2570
  var TIER_1_DOCS = ["Test_Policy.md", "Test_Strategy.md", "Test_Architecture.md"];
2500
2571
  async function runValidation(projectPath) {
2501
2572
  const warnings = [];
@@ -2543,7 +2614,8 @@ var SECTION_RUNNERS = [
2543
2614
  { key: "2f", run: syncCiTemplates },
2544
2615
  { key: "2g", run: syncGitignore },
2545
2616
  { key: "2h", run: syncSdlcEngine },
2546
- { key: "2i", run: syncWorkflows }
2617
+ { key: "2i", run: syncWorkflows },
2618
+ { key: "2j", run: verifyBranchProtection }
2547
2619
  ];
2548
2620
  async function syncProject(projectPath) {
2549
2621
  const absPath = resolve(projectPath);