From 378cbd52bca2db119e42d1c23654619633fd81ff Mon Sep 17 00:00:00 2001 From: thoroc Date: Tue, 9 Dec 2025 21:18:49 +0000 Subject: [PATCH] feat: add repository settings check workflow Implement automated GitHub Actions workflow to validate repository settings: - Check default branch is 'main' - Verify branch protection is enabled on 'main' - Confirm automatic branch deletion after merge is enabled Features: - Scheduled weekly checks (Monday midnight UTC) - Manual dispatch option for ad-hoc verification - Automated GitHub issue creation/update/closure - Retry logic with exponential backoff for transient failures - GitHub Actions summary with remediation steps - Local testing support with bun Implementation: - TypeScript script using @octokit/rest for GitHub API - Read-only verification (never modifies settings) - Idempotent issue handling to avoid duplicates - Exit 0 for check failures, exit 1 for script errors Ref: .context/features/check-repo-branch-settings.md --- .github/scripts/check-repo-settings.ts | 294 ++++++++++++++++++++++ .github/workflows/check-repo-settings.yml | 39 +++ bun.lock | 39 ++- package.json | 2 + 4 files changed, 370 insertions(+), 4 deletions(-) create mode 100755 .github/scripts/check-repo-settings.ts create mode 100644 .github/workflows/check-repo-settings.yml diff --git a/.github/scripts/check-repo-settings.ts b/.github/scripts/check-repo-settings.ts new file mode 100755 index 0000000..4e84386 --- /dev/null +++ b/.github/scripts/check-repo-settings.ts @@ -0,0 +1,294 @@ +#!/usr/bin/env bun +/** + * GitHub Repository Settings Checker + * + * This script validates repository settings to ensure: + * 1. Default branch is 'main' + * 2. Branch protection is enabled on 'main' + * 3. Automatic branch deletion after merge is enabled + * + * The script is read-only and reports issues rather than fixing them. + * Issues are automatically created/updated/closed based on check results. + */ + +import { Octokit } from '@octokit/rest'; + +interface CheckResult { + name: string; + passed: boolean; + current: string; + expected: string; + remediation?: string; +} + +/** + * Retry logic for transient API failures + * Does not retry on 403 Forbidden (permanent permission issues) + */ +async function withRetry(fn: () => Promise, maxRetries: number = 3): Promise { + for (let i = 0; i < maxRetries; i++) { + try { + return await fn(); + } catch (error: any) { + // Don't retry on 403 Forbidden (permanent permission issue) + if (i === maxRetries - 1 || error.status === 403) { + throw error; + } + // Exponential backoff: 1s, 2s, 3s + await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1))); + } + } + throw new Error('Unreachable'); +} + +/** + * Generate markdown summary for GitHub Actions + */ +function generateSummary(results: CheckResult[], allPassed: boolean, issueAction?: string): string { + let summary = `## ${allPassed ? '✅' : '⚠️'} Repository Settings Check${allPassed ? ' - All Passed' : ' - Issues Found'}\n\n`; + + for (const result of results) { + const icon = result.passed ? '✅' : '❌'; + summary += `- ${icon} **${result.name}**: ${result.current}\n`; + if (!result.passed && result.remediation) { + summary += ` - Expected: ${result.expected}\n`; + summary += ` - Action: ${result.remediation}\n`; + } + } + + summary += '\n'; + summary += allPassed + ? 'Repository configuration complies with organizational standards.\n' + : 'Please review and update repository settings.\n'; + + if (issueAction) { + summary += `\n${issueAction}\n`; + } + + return summary; +} + +/** + * Generate issue body with failed checks + */ +function generateIssueBody(failedChecks: CheckResult[]): string { + let body = '## ⚠️ Repository Settings Check Failed\n\n'; + body += 'The automated repository settings check has detected configuration issues that need attention.\n\n'; + body += '### Failed Checks\n\n'; + + for (const check of failedChecks) { + body += `- [ ] **${check.name}**\n`; + body += ` - Current: ${check.current}\n`; + body += ` - Expected: ${check.expected}\n`; + body += ` - Action: ${check.remediation}\n\n`; + } + + body += '### Next Steps\n\n'; + body += '1. Review the failed checks above\n'; + body += '2. Follow the remediation steps for each failed check\n'; + body += '3. This issue will automatically close when all checks pass\n\n'; + body += '---\n'; + body += `_This issue was automatically created by the repository settings check workflow._`; + + return body; +} + +/** + * Handle GitHub issue creation/update/closure + */ +async function handleIssue( + octokit: Octokit, + owner: string, + repo: string, + results: CheckResult[], + allPassed: boolean, +): Promise { + const issueTitle = 'Repository Settings Check Failed'; + + // Search for existing open issue + const issuesResponse = await withRetry(() => + octokit.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: 'repository-settings', + creator: 'github-actions[bot]', + }), + ); + + const issues = issuesResponse.data; + const existingIssue = issues.find((issue) => issue.title === issueTitle); + + if (allPassed) { + // All checks passed - close issue if it exists + if (existingIssue) { + await withRetry(() => + octokit.rest.issues.createComment({ + owner, + repo, + issue_number: existingIssue.number, + body: '✅ All repository settings checks are now passing. Closing this issue.', + }), + ); + + await withRetry(() => + octokit.rest.issues.update({ + owner, + repo, + issue_number: existingIssue.number, + state: 'closed', + }), + ); + + return `Note: Closed issue #${existingIssue.number} as all checks are now passing.`; + } + return ''; + } else { + // Some checks failed - create or update issue + const failedChecks = results.filter((r) => !r.passed); + const issueBody = generateIssueBody(failedChecks); + + if (existingIssue) { + // Update existing issue + await withRetry(() => + octokit.rest.issues.update({ + owner, + repo, + issue_number: existingIssue.number, + body: issueBody, + }), + ); + + await withRetry(() => + octokit.rest.issues.createComment({ + owner, + repo, + issue_number: existingIssue.number, + body: `⚠️ Repository settings check ran on ${new Date().toISOString()} - issues still present.`, + }), + ); + + return `Note: Updated existing issue #${existingIssue.number} with current findings.`; + } else { + // Create new issue + const newIssueResponse = await withRetry(() => + octokit.rest.issues.create({ + owner, + repo, + title: issueTitle, + body: issueBody, + labels: ['chore', 'repository-settings'], + }), + ); + + return `Note: Created issue #${newIssueResponse.data.number} to track these configuration issues.`; + } + } +} + +/** + * Main function to check repository settings + */ +async function checkRepositorySettings(): Promise { + // Validate environment variables + const token = process.env.GITHUB_TOKEN; + const owner = process.env.REPO_OWNER; + const repo = process.env.REPO_NAME; + + if (!token || !owner || !repo) { + console.error('❌ Missing required environment variables: GITHUB_TOKEN, REPO_OWNER, REPO_NAME'); + process.exit(1); + } + + const octokit = new Octokit({ auth: token }); + + try { + // Fetch repository data + const repoResponse = await withRetry(() => octokit.rest.repos.get({ owner, repo })); + const repoData = repoResponse.data; + + // Check 1: Default branch + const isMainDefault = repoData.default_branch === 'main'; + + // Check 2: Branch protection + let hasProtection = false; + try { + await withRetry(() => + octokit.rest.repos.getBranchProtection({ + owner, + repo, + branch: 'main', + }), + ); + hasProtection = true; + } catch (error: any) { + // 404 means no protection rules exist + if (error.status !== 404) { + throw error; + } + } + + // Check 3: Auto-delete branches + const autoDelete = repoData.delete_branch_on_merge === true; + + // Structure results + const results: CheckResult[] = [ + { + name: 'Default branch', + passed: isMainDefault, + current: repoData.default_branch, + expected: 'main', + remediation: isMainDefault ? undefined : 'Go to Settings → Branches → Change default branch to main', + }, + { + name: 'Branch protection', + passed: hasProtection, + current: hasProtection ? 'Enabled' : 'No protection rules', + expected: 'Protection enabled', + remediation: hasProtection ? undefined : 'Go to Settings → Branches → Add rule for main', + }, + { + name: 'Automatic branch deletion', + passed: autoDelete, + current: autoDelete ? 'Enabled' : 'Disabled', + expected: 'Enabled', + remediation: autoDelete ? undefined : 'Go to Settings → General → Enable "Automatically delete head branches"', + }, + ]; + + // Determine if all checks passed + const allPassed = results.every((r) => r.passed); + + // Handle GitHub issue creation/update/closure + const issueAction = await handleIssue(octokit, owner, repo, results, allPassed); + + // Generate summary + const summary = generateSummary(results, allPassed, issueAction); + + // Write to GITHUB_STEP_SUMMARY if in GitHub Actions + if (process.env.GITHUB_STEP_SUMMARY) { + await Bun.write(process.env.GITHUB_STEP_SUMMARY, summary); + } + + // Always log to console for local testing + console.log(summary); + + // Exit 0 even if checks fail (informational only) + // Script errors (caught by catch block) will exit 1 + process.exit(0); + } catch (error: any) { + // Handle script errors (API failures, network issues, permission problems) + console.error('❌ Script error:', error.message); + if (error.status === 403) { + console.error('Permission denied. Ensure GITHUB_TOKEN has read permissions for repository settings.'); + } + // Exit 1 for script errors + process.exit(1); + } +} + +// Main execution +checkRepositorySettings().catch((error) => { + console.error('❌ Unexpected error:', error); + process.exit(1); +}); diff --git a/.github/workflows/check-repo-settings.yml b/.github/workflows/check-repo-settings.yml new file mode 100644 index 0000000..9900226 --- /dev/null +++ b/.github/workflows/check-repo-settings.yml @@ -0,0 +1,39 @@ +name: Check Repository Settings + +# Scheduled workflow to detect configuration drift in repository settings +# Runs weekly to ensure branch protection and cleanup settings are maintained + +on: + schedule: + # Weekly on Monday at midnight UTC to detect configuration drift + # Weekly frequency is sufficient for drift detection while minimizing API usage + - cron: '0 0 * * 1' + workflow_dispatch: # Allow manual triggering for ad-hoc checks + +permissions: + contents: read # For reading repository settings and branch protection + issues: write # For creating/updating/closing issues + +jobs: + check-settings: + name: Verify Branch Configuration + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run repository settings check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO_OWNER: ${{ github.repository_owner }} + REPO_NAME: ${{ github.event.repository.name }} + run: bun run check:repo-settings diff --git a/bun.lock b/bun.lock index 8233de3..f8b33e4 100644 --- a/bun.lock +++ b/bun.lock @@ -13,6 +13,7 @@ "@nx/js": "22.1.3", "@nx/node": "^22.1.3", "@nx/plugin": "^22.1.3", + "@octokit/rest": "^20.0.0", "@opencode-ai/plugin": "^1.0.133", "@swc-node/register": "^1.11.1", "@swc/cli": "~0.6.0", @@ -715,6 +716,30 @@ "@nx/workspace": ["@nx/workspace@22.1.3", "", { "dependencies": { "@nx/devkit": "22.1.3", "@zkochan/js-yaml": "0.0.7", "chalk": "^4.1.0", "enquirer": "~2.3.6", "nx": "22.1.3", "picomatch": "4.0.2", "semver": "^7.6.3", "tslib": "^2.3.0", "yargs-parser": "21.1.1" } }, "sha512-VthMD2e4JV8w6fysIHCa/6ZauHSJXT7m2lVIglvK14cJcSfUHuQMUB7KhluH7x7UIEwSOXToCh28YLhexIlr9A=="], + "@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="], + + "@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="], + + "@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="], + + "@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="], + + "@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], + + "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@11.4.4-cjs.2", "", { "dependencies": { "@octokit/types": "^13.7.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw=="], + + "@octokit/plugin-request-log": ["@octokit/plugin-request-log@4.0.1", "", { "peerDependencies": { "@octokit/core": "5" } }, "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA=="], + + "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@13.3.2-cjs.1", "", { "dependencies": { "@octokit/types": "^13.8.0" }, "peerDependencies": { "@octokit/core": "^5" } }, "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ=="], + + "@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="], + + "@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="], + + "@octokit/rest": ["@octokit/rest@20.1.2", "", { "dependencies": { "@octokit/core": "^5.0.2", "@octokit/plugin-paginate-rest": "11.4.4-cjs.2", "@octokit/plugin-request-log": "^4.0.0", "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1" } }, "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA=="], + + "@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + "@opencode-ai/plugin": ["@opencode-ai/plugin@1.0.133", "", { "dependencies": { "@opencode-ai/sdk": "1.0.133", "zod": "4.1.8" } }, "sha512-ZsUE7D7+lMrmBmhotTcUnUZlgGadhL0dIhSu/Zvj7/W9wn8fGSe+oGVp5sOLhCYbacjJrOUOeHgpz9XwwrFUXA=="], "@opencode-ai/sdk": ["@opencode-ai/sdk@1.0.133", "", {}, "sha512-kM+VJJ09SU51aruQ78DSy+6CjNc4wMytvGBrZ1IIJ8etUIdGA59wrnIOSxBVs4u/Gb9pjjgsF8sWp59UdLWP9w=="], @@ -921,6 +946,8 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], @@ -1267,6 +1294,8 @@ "bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="], + "before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="], + "bin-version": ["bin-version@6.0.0", "", { "dependencies": { "execa": "^5.0.0", "find-versions": "^5.0.0" } }, "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw=="], "bin-version-check": ["bin-version-check@5.1.0", "", { "dependencies": { "bin-version": "^6.0.0", "semver": "^7.5.3", "semver-truncate": "^3.0.0" } }, "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g=="], @@ -1543,6 +1572,8 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], @@ -3043,6 +3074,8 @@ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], + "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], "unstorage": ["unstorage@1.17.3", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q=="], @@ -3239,8 +3272,6 @@ "@pantheon-org/opencode-docs-builder/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@pantheon-org/opencode-warcraft-notifications-plugin/@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], - "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@rollup/pluginutils/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], @@ -3249,6 +3280,8 @@ "@swc/cli/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@types/bun/bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -3549,8 +3582,6 @@ "@pantheon-org/opencode-docs-builder/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@pantheon-org/opencode-warcraft-notifications-plugin/@types/bun/bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], - "@swc-node/sourcemap-support/source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "@unrs/resolver-binding-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], diff --git a/package.json b/package.json index adf7374..6660fa9 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "validate": "bun run format:check && bun run lint:md && bun run lint && bun run type-check && bun run test && bun run build", "validate:affected": "bun run format:check && bun run lint:md && bun run lint:affected && bun run type-check:affected && bun run test:affected && bun run build:affected", "generate:plugin": "nx g ./tools/generators:plugin", + "check:repo-settings": "bun run .github/scripts/check-repo-settings.ts", "hooks:install": "bunx lefthook install", "hooks:uninstall": "bunx lefthook uninstall" }, @@ -35,6 +36,7 @@ "@nx/js": "22.1.3", "@nx/node": "^22.1.3", "@nx/plugin": "^22.1.3", + "@octokit/rest": "^20.0.0", "@opencode-ai/plugin": "^1.0.133", "@swc-node/register": "^1.11.1", "@swc/cli": "~0.6.0",