mirror of
https://github.com/dtzp555-max/clawkeeper.git
synced 2026-07-21 21:15:06 +00:00
repo-rule-detector: summarize rulesets PR parameters
This commit is contained in:
@@ -67,6 +67,71 @@ clawkeeper restore-apply ~/openclaw-backup.tgz
|
|||||||
clawkeeper restore-apply ~/openclaw-backup.tgz --restart-gateway
|
clawkeeper restore-apply ~/openclaw-backup.tgz --restart-gateway
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Repo rule detector (script)
|
||||||
|
If you need to quickly see what GitHub repo rules might block merges (repo rulesets + classic branch protection), run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node scripts/repo-rule-detector.mjs dtzp555-max/ocm --branch main
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output (dtzp555-max/ocm):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repo": "dtzp555-max/ocm",
|
||||||
|
"url": "https://github.com/dtzp555-max/ocm",
|
||||||
|
"defaultBranch": "main",
|
||||||
|
"branch": "main",
|
||||||
|
"allowedMergeMethods": {
|
||||||
|
"mergeCommit": true,
|
||||||
|
"squash": true,
|
||||||
|
"rebase": true
|
||||||
|
},
|
||||||
|
"rulesetsRulePresence": {
|
||||||
|
"required_linear_history": true,
|
||||||
|
"non_fast_forward": true,
|
||||||
|
"deletion": true
|
||||||
|
},
|
||||||
|
"branchProtection": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"rulesets": [
|
||||||
|
{
|
||||||
|
"id": 13362557,
|
||||||
|
"name": "ocm",
|
||||||
|
"enforcement": "active",
|
||||||
|
"target": null,
|
||||||
|
"rules": {
|
||||||
|
"pull_request": {
|
||||||
|
"present": true,
|
||||||
|
"parameters": {
|
||||||
|
"required_review_thread_resolution": true,
|
||||||
|
"allowed_merge_methods": [
|
||||||
|
"merge",
|
||||||
|
"squash",
|
||||||
|
"rebase"
|
||||||
|
],
|
||||||
|
"required_approving_review_count": 0,
|
||||||
|
"require_code_owner_review": false,
|
||||||
|
"dismiss_stale_reviews_on_push": false,
|
||||||
|
"require_last_push_approval": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required_linear_history": {
|
||||||
|
"present": true
|
||||||
|
},
|
||||||
|
"non_fast_forward": {
|
||||||
|
"present": true
|
||||||
|
},
|
||||||
|
"deletion": {
|
||||||
|
"present": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Safety notes
|
## Safety notes
|
||||||
This tool edits `~/.openclaw/openclaw.json`.
|
This tool edits `~/.openclaw/openclaw.json`.
|
||||||
- Always makes a timestamped backup before modifying config.
|
- Always makes a timestamped backup before modifying config.
|
||||||
|
|||||||
@@ -20,7 +20,18 @@
|
|||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
|
|
||||||
function sh(args) {
|
function sh(args) {
|
||||||
return execFileSync('gh', args, { encoding: 'utf8' }).trim();
|
// Keep stdout machine-readable: never let gh chatter leak into our output.
|
||||||
|
return execFileSync('gh', args, {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe']
|
||||||
|
}).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pick(obj, keys) {
|
||||||
|
if (!obj) return null;
|
||||||
|
const out = {};
|
||||||
|
for (const k of keys) out[k] = obj?.[k] ?? null;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseArgs(argv) {
|
function parseArgs(argv) {
|
||||||
@@ -57,18 +68,58 @@ function main() {
|
|||||||
rulesets = [];
|
rulesets = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PULL_REQUEST_PARAM_KEYS = [
|
||||||
|
'required_review_thread_resolution',
|
||||||
|
'allowed_merge_methods',
|
||||||
|
'required_approving_review_count',
|
||||||
|
'require_code_owner_review',
|
||||||
|
'dismiss_stale_reviews_on_push',
|
||||||
|
'require_last_push_approval'
|
||||||
|
];
|
||||||
|
|
||||||
|
function fetchRulesetDetails(rulesetId) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(sh(['api', `repos/${repo}/rulesets/${rulesetId}`]));
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasRuleType(rules, type) {
|
||||||
|
return Array.isArray(rules) && rules.some(r => r?.type === type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeRulesetRules(rules) {
|
||||||
|
const prRule = Array.isArray(rules) ? rules.find(r => r?.type === 'pull_request') : null;
|
||||||
|
const prParams = prRule?.parameters ? pick(prRule.parameters, PULL_REQUEST_PARAM_KEYS) : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
pull_request: prParams
|
||||||
|
? { present: true, parameters: prParams }
|
||||||
|
: { present: false, parameters: null },
|
||||||
|
required_linear_history: { present: hasRuleType(rules, 'required_linear_history') },
|
||||||
|
non_fast_forward: { present: hasRuleType(rules, 'non_fast_forward') },
|
||||||
|
deletion: { present: hasRuleType(rules, 'deletion') }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const relevantRulesets = rulesets
|
const relevantRulesets = rulesets
|
||||||
.filter(rs => rs?.enforcement && rs.enforcement !== 'disabled')
|
.filter(rs => rs?.enforcement && rs.enforcement !== 'disabled')
|
||||||
.filter(rs => {
|
.filter(rs => {
|
||||||
const targets = rs?.conditions?.ref_name?.include ?? [];
|
const targets = rs?.conditions?.ref_name?.include ?? [];
|
||||||
return targets.length === 0 || targets.includes('~DEFAULT_BRANCH') || targets.includes(branch) || targets.includes(`refs/heads/${branch}`);
|
return targets.length === 0 || targets.includes('~DEFAULT_BRANCH') || targets.includes(branch) || targets.includes(`refs/heads/${branch}`);
|
||||||
})
|
})
|
||||||
.map(rs => ({
|
.map(rs => {
|
||||||
id: rs.id,
|
const details = fetchRulesetDetails(rs.id);
|
||||||
name: rs.name,
|
const rulesSummary = summarizeRulesetRules(details?.rules ?? []);
|
||||||
enforcement: rs.enforcement,
|
return {
|
||||||
target: rs?.conditions?.ref_name ?? null
|
id: rs.id,
|
||||||
}));
|
name: rs.name,
|
||||||
|
enforcement: rs.enforcement,
|
||||||
|
target: rs?.conditions?.ref_name ?? null,
|
||||||
|
rules: rulesSummary
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const allowedMergeMethods = {
|
const allowedMergeMethods = {
|
||||||
mergeCommit: !!repoInfo.allow_merge_commit,
|
mergeCommit: !!repoInfo.allow_merge_commit,
|
||||||
@@ -76,12 +127,19 @@ function main() {
|
|||||||
rebase: !!repoInfo.allow_rebase_merge
|
rebase: !!repoInfo.allow_rebase_merge
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rulesetsRulePresence = {
|
||||||
|
required_linear_history: relevantRulesets.some(rs => rs?.rules?.required_linear_history?.present),
|
||||||
|
non_fast_forward: relevantRulesets.some(rs => rs?.rules?.non_fast_forward?.present),
|
||||||
|
deletion: relevantRulesets.some(rs => rs?.rules?.deletion?.present)
|
||||||
|
};
|
||||||
|
|
||||||
const out = {
|
const out = {
|
||||||
repo: repoInfo.full_name,
|
repo: repoInfo.full_name,
|
||||||
url: repoInfo.html_url,
|
url: repoInfo.html_url,
|
||||||
defaultBranch: repoInfo.default_branch,
|
defaultBranch: repoInfo.default_branch,
|
||||||
branch,
|
branch,
|
||||||
allowedMergeMethods,
|
allowedMergeMethods,
|
||||||
|
rulesetsRulePresence,
|
||||||
branchProtection: bp ? {
|
branchProtection: bp ? {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
requiredStatusChecks: bp.required_status_checks ? {
|
requiredStatusChecks: bp.required_status_checks ? {
|
||||||
|
|||||||
Reference in New Issue
Block a user