@metasession.co/devaudit-cli 0.3.4 → 0.3.5

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.5"};
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,22 @@ 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
1736
  "Compliance Validation",
1738
1737
  "DevAudit Release Approval",
1739
1738
  "Quality Gates"
1740
1739
  ];
1740
+ var MAIN_REVIEW_COUNT = 1;
1741
+ var DEVELOP_REVIEW_COUNT = 0;
1742
+ async function resolveIntegrationBranch(projectPath) {
1743
+ try {
1744
+ const raw = await promises.readFile(join(projectPath, "sdlc-config.json"), "utf-8");
1745
+ const config = JSON.parse(raw);
1746
+ return config.integration_branch ?? "develop";
1747
+ } catch {
1748
+ return "develop";
1749
+ }
1750
+ }
1741
1751
  async function configureBranchProtection(ctx, provider) {
1742
1752
  if (ctx.installMode === "developer") {
1743
1753
  return {
@@ -1757,25 +1767,34 @@ async function configureBranchProtection(ctx, provider) {
1757
1767
  };
1758
1768
  }
1759
1769
  const repo = `${meta.owner}/${meta.name}`;
1770
+ const integrationBranch = await resolveIntegrationBranch(ctx.projectPath);
1760
1771
  if (ctx.dryRun) {
1761
1772
  return {
1762
1773
  step: "9/11 Configure branch protection",
1763
1774
  status: "planned",
1764
- message: `would apply branch protection on ${repo}:${meta.defaultBranch} with checks=${JSON.stringify(REQUIRED_CHECKS)}`
1775
+ message: `would apply branch protection on ${repo}:${meta.defaultBranch} (1 review) + ${integrationBranch} (0 reviews) with checks=${JSON.stringify(REQUIRED_CHECKS)}`
1765
1776
  };
1766
1777
  }
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
- };
1778
+ const results = [];
1779
+ const mainResult = await provider.applyBranchProtection(ctx.projectPath, meta.defaultBranch, REQUIRED_CHECKS, { requiredReviewCount: MAIN_REVIEW_COUNT });
1780
+ if (mainResult.applied) {
1781
+ results.push(`${meta.defaultBranch}: ok (${MAIN_REVIEW_COUNT} review)`);
1782
+ } else {
1783
+ results.push(`${meta.defaultBranch}: FAILED \u2014 ${mainResult.message ?? "unknown"}`);
1774
1784
  }
1785
+ if (integrationBranch !== meta.defaultBranch) {
1786
+ const devResult = await provider.applyBranchProtection(ctx.projectPath, integrationBranch, REQUIRED_CHECKS, { requiredReviewCount: DEVELOP_REVIEW_COUNT });
1787
+ if (devResult.applied) {
1788
+ results.push(`${integrationBranch}: ok (${DEVELOP_REVIEW_COUNT} reviews)`);
1789
+ } else {
1790
+ results.push(`${integrationBranch}: FAILED \u2014 ${devResult.message ?? "unknown"}`);
1791
+ }
1792
+ }
1793
+ const allOk = results.every((r) => r.includes(": ok"));
1775
1794
  return {
1776
1795
  step: "9/11 Configure branch protection",
1777
- status: "warn",
1778
- message: `${result.message ?? "branch-protection apply failed"} \u2014 configure manually`
1796
+ status: allOk ? "ok" : "warn",
1797
+ message: allOk ? results.join(" | ") : `${results.join(" | ")} \u2014 configure manually`
1779
1798
  };
1780
1799
  }
1781
1800
  var ajv = new Ajv({ allErrors: true, strict: false });
@@ -2496,6 +2515,60 @@ async function syncWorkflows(ctx) {
2496
2515
  message: files.length > 0 ? `synced to .devin/workflows/` : "no workflow files found"
2497
2516
  };
2498
2517
  }
2518
+ var REQUIRED_CHECKS2 = [
2519
+ "Compliance Validation",
2520
+ "DevAudit Release Approval",
2521
+ "Quality Gates"
2522
+ ];
2523
+ var MAIN_REVIEW_COUNT2 = 1;
2524
+ var DEVELOP_REVIEW_COUNT2 = 0;
2525
+ async function verifyBranchProtection(ctx) {
2526
+ const config = await readSdlcConfig(ctx.projectPath);
2527
+ const integrationBranch = config?.integration_branch ?? "develop";
2528
+ config?.release_branch ?? "main";
2529
+ let provider;
2530
+ try {
2531
+ provider = await getGitProvider(ctx.projectPath);
2532
+ } catch (err) {
2533
+ return {
2534
+ name: "Branch protection",
2535
+ filesSynced: 0,
2536
+ warning: `git provider unavailable (${err.message}) \u2014 verify manually`
2537
+ };
2538
+ }
2539
+ let meta;
2540
+ try {
2541
+ meta = await provider.getRepoMeta(ctx.projectPath);
2542
+ } catch (err) {
2543
+ return {
2544
+ name: "Branch protection",
2545
+ filesSynced: 0,
2546
+ warning: `could not resolve repo (${err.message}) \u2014 verify manually`
2547
+ };
2548
+ }
2549
+ const results = [];
2550
+ const mainResult = await provider.applyBranchProtection(ctx.projectPath, meta.defaultBranch, REQUIRED_CHECKS2, { requiredReviewCount: MAIN_REVIEW_COUNT2 });
2551
+ if (mainResult.applied) {
2552
+ results.push(`${meta.defaultBranch}: ok`);
2553
+ } else {
2554
+ results.push(`${meta.defaultBranch}: failed \u2014 ${mainResult.message ?? "unknown"}`);
2555
+ }
2556
+ if (integrationBranch !== meta.defaultBranch) {
2557
+ const devResult = await provider.applyBranchProtection(ctx.projectPath, integrationBranch, REQUIRED_CHECKS2, { requiredReviewCount: DEVELOP_REVIEW_COUNT2 });
2558
+ if (devResult.applied) {
2559
+ results.push(`${integrationBranch}: ok`);
2560
+ } else {
2561
+ results.push(`${integrationBranch}: failed \u2014 ${devResult.message ?? "unknown"}`);
2562
+ }
2563
+ }
2564
+ const allOk = results.every((r) => r.endsWith(": ok"));
2565
+ return {
2566
+ name: "Branch protection",
2567
+ filesSynced: 0,
2568
+ message: results.join(" | "),
2569
+ warning: allOk ? void 0 : "some branches failed \u2014 verify manually"
2570
+ };
2571
+ }
2499
2572
  var TIER_1_DOCS = ["Test_Policy.md", "Test_Strategy.md", "Test_Architecture.md"];
2500
2573
  async function runValidation(projectPath) {
2501
2574
  const warnings = [];
@@ -2543,7 +2616,8 @@ var SECTION_RUNNERS = [
2543
2616
  { key: "2f", run: syncCiTemplates },
2544
2617
  { key: "2g", run: syncGitignore },
2545
2618
  { key: "2h", run: syncSdlcEngine },
2546
- { key: "2i", run: syncWorkflows }
2619
+ { key: "2i", run: syncWorkflows },
2620
+ { key: "2j", run: verifyBranchProtection }
2547
2621
  ];
2548
2622
  async function syncProject(projectPath) {
2549
2623
  const absPath = resolve(projectPath);