Skip to content

chore(deps): update turbo monorepo to v2.9.18 #155

chore(deps): update turbo monorepo to v2.9.18

chore(deps): update turbo monorepo to v2.9.18 #155

name: CNC Sync Policy
on:
workflow_dispatch:
pull_request:
branches:
- master
types:
- opened
- edited
- synchronize
- reopened
- ready_for_review
concurrency:
group: cnc-sync-policy-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
policy-check:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
pull-requests: read
issues: read
steps:
- name: Validate CNC sync policy
uses: actions/github-script@v8
with:
script: |
const pr = context.payload.pull_request;
if (!pr) return;
const body = pr.body || '';
const title = pr.title || '';
const combined = `${title}\n${body}`;
const errors = [];
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
per_page: 100,
});
const filenames = files.map((file) => file.filename);
const touchesCncPaths = filenames.some((name) =>
/^apps\/cnc\/src\//.test(name) || /^packages\/protocol\/src\//.test(name)
);
const isCncFeature = /\[(x|X)\]\s+This PR is a CNC feature change\./.test(body);
const requiredReviewPassChecks = [
/\[(x|X)\]\s+I completed a final review pass after my latest implementation commit\b/,
/\[(x|X)\]\s+I reviewed the final diff for correctness, scope control, and regression risk\./,
/\[(x|X)\]\s+I addressed all review comments\/threads with follow-up commits or explicit rationale\./,
/\[(x|X)\]\s+I re-reviewed the updated diff after applying review feedback\./,
];
for (const check of requiredReviewPassChecks) {
if (!check.test(body)) {
errors.push(`Missing required checked review-pass checklist item matching: ${check}`);
}
}
// Low-cost CI parity for commit-msg hook: validate PR commit subjects.
const conventionalCommitRegex =
/^(revert: )?(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([^)]+\))?!?: .+/;
const commits = await github.paginate(github.rest.pulls.listCommits, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
per_page: 100,
});
const invalidCommitSubjects = [];
for (const commit of commits) {
const subject = (commit.commit?.message || '').split('\n')[0].trim();
if (!subject || /^Merge\b/.test(subject)) {
continue;
}
if (!conventionalCommitRegex.test(subject)) {
invalidCommitSubjects.push(`${commit.sha.slice(0, 7)}: ${subject}`);
}
}
if (invalidCommitSubjects.length > 0) {
errors.push(
`Non-conventional commit subjects detected: ${invalidCommitSubjects.slice(0, 5).join(' | ')}`
);
}
// Low-cost CI parity for pre-commit secret scanning: scan added diff lines for common secret patterns.
const secretPatterns = [
{
name: 'Private key',
regex: /-----BEGIN(?: RSA| EC| OPENSSH| DSA)? PRIVATE KEY-----/,
},
{
name: 'GitHub token',
regex: /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/,
},
{
name: 'AWS access key',
regex: /\bAKIA[0-9A-Z]{16}\b/,
},
{
name: 'Google API key',
regex: /\bAIza[0-9A-Za-z\-_]{35}\b/,
},
{
name: 'Slack token',
regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
},
{
name: 'Generic credential assignment',
regex: /(api[_-]?key|token|secret|password)\s*[:=]\s*["'][^"'\s]{16,}["']/i,
},
];
const placeholderRegex =
/(example|sample|dummy|fake|test|changeme|placeholder|redacted|your[_-]?token|your[_-]?key)/i;
const secretHits = [];
for (const file of files) {
if (!file.patch) {
continue;
}
const addedLines = file.patch
.split('\n')
.filter((line) => line.startsWith('+') && !line.startsWith('+++'))
.map((line) => line.slice(1));
for (const line of addedLines) {
if (placeholderRegex.test(line)) {
continue;
}
const match = secretPatterns.find((pattern) => pattern.regex.test(line));
if (match) {
secretHits.push(`${file.filename}: ${match.name}`);
break;
}
}
}
if (secretHits.length > 0) {
errors.push(
`Potential secrets detected in added lines: ${secretHits.slice(0, 5).join(' | ')}`
);
}
if (touchesCncPaths && !isCncFeature) {
errors.push('This PR touches CNC/protocol source paths. Check "This PR is a CNC feature change." in the PR template.');
}
if (isCncFeature) {
const requiredFields = [
{
name: 'Protocol issue',
regex: /Protocol issue:\s*(kaonis\/woly-server#\d+|https:\/\/github\.com\/kaonis\/woly-server\/issues\/\d+)/i,
},
{
name: 'Backend issue',
regex: /Backend issue:\s*(kaonis\/woly-server#\d+|https:\/\/github\.com\/kaonis\/woly-server\/issues\/\d+)/i,
},
{
name: 'Frontend issue',
regex: /Frontend issue:\s*(kaonis\/woly#\d+|https:\/\/github\.com\/kaonis\/woly\/issues\/\d+)/i,
},
];
for (const field of requiredFields) {
if (!field.regex.test(body)) {
errors.push(`Missing or invalid ${field.name}. Use explicit cross-repo reference format.`);
}
}
const requiredChecks = [
/\[(x|X)\]\s+Protocol contract updated or verified\./,
/\[(x|X)\]\s+Backend endpoint\/command implemented or explicitly unchanged\./,
/\[(x|X)\]\s+Frontend integration implemented or tracked in linked issue\./,
/\[(x|X)\]\s+Local validation passed/,
];
for (const check of requiredChecks) {
if (!check.test(body)) {
errors.push(`Missing required checked checklist item matching: ${check}`);
}
}
const capabilityIssue = await github.rest.issues.get({
owner: 'kaonis',
repo: 'woly-server',
issue_number: 254,
});
const capabilityMentioned =
/kaonis\/woly-server#254/.test(combined) ||
/https:\/\/github\.com\/kaonis\/woly-server\/issues\/254/.test(combined) ||
/\b#254\b/.test(combined);
if (capabilityIssue.data.state !== 'closed' && !capabilityMentioned) {
errors.push('Issue kaonis/woly-server#254 is still open. CNC feature PRs must implement/link this capability work first.');
}
}
if (errors.length > 0) {
core.setFailed(`CNC sync policy check failed:\n- ${errors.join('\n- ')}`);
}