Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "ChittyConnect Sync / sync"
"context": "sync"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CodeQL / Analyze"
"context": "CodeQL"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "PR Validation / validate"
"context": "validate"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "ChittyConnect Sync / sync"
"context": "sync"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "ChittyOS Compliance Check / compliance"
"context": "compliance"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "ChittyConnect Sync / sync"
"context": "sync"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CI / lint-and-format"
"context": "lint-and-format"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CI / test-and-build"
"context": "test-and-build"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CI / test"
"context": "test"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "Compliance Check / compliance"
"context": "compliance"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "ChittyConnect Sync / sync"
"context": "sync"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CI / lint-and-test"
"context": "lint-and-test"
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion compliance/rulesets/orgs/chittyos/10-require-ci-test.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CI / test"
"context": "test"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CodeQL / Analyze"
"context": "CodeQL"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "Compliance Check / compliance"
"context": "compliance"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"strict_required_status_checks_policy": true,
"required_status_checks": [
{
"context": "CI / build"
"context": "build"
}
]
}
Expand Down
8 changes: 6 additions & 2 deletions scripts/apply-repo-settings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ apply_repo_settings() {
return 0
fi

if gh api "repos/$org/$repo" --method PATCH --input <(printf '%s' "$payload") >/dev/null 2>/tmp/repo_patch_err.log; then
local errfile
errfile="$(mktemp)"
if gh api "repos/$org/$repo" --method PATCH --input <(printf '%s' "$payload") >/dev/null 2>"$errfile"; then
rm -f "$errfile"
echo "patched $org/$repo"
return 0
fi

echo "failed $org/$repo"
sed -n '1,2p' /tmp/repo_patch_err.log
sed -n '1,2p' "$errfile"
rm -f "$errfile"
return 1
}

Expand Down
42 changes: 39 additions & 3 deletions scripts/audit-org-webhooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,18 @@ if ! gh auth status >/dev/null 2>&1; then
exit 1
fi

if ! gh auth status -t 2>/dev/null | grep -q 'admin:org_hook'; then
echo "Missing admin:org_hook scope. Run: gh auth refresh -h github.com -s admin:org_hook"
org_probe="${orgs[0]}"
probe_status="$(gh api -X GET -H 'Accept: application/vnd.github+json' "/orgs/$org_probe/hooks" -i 2>/dev/null | awk 'NR==1 {print $2}')"
if [ -z "$probe_status" ]; then
echo "Failed to verify GitHub token permissions for org '$org_probe'"
exit 1
fi
if [ "$probe_status" = "403" ]; then
echo "Token lacks permission to list org hooks for '$org_probe'"
exit 1
fi
if [ "$probe_status" != "200" ]; then
echo "Unexpected HTTP status '$probe_status' when verifying org hook access for '$org_probe'"
exit 1
fi

Expand Down Expand Up @@ -115,9 +125,19 @@ for org in "${orgs[@]}"; do

needs_apply_reasons=()

# Validate event scope unless wildcard is used intentionally.
# Validate event scope against the required event set.
if [ "$events_joined" = "*" ]; then
needs_apply_reasons+=("wildcard-events")
else
sorted_events_joined="$(
tr ',' '\n' <<<"$events_joined" | sed '/^$/d' | sort -u | tr '\n' ',' | sed 's/,$//'
)"
sorted_required_events="$(
tr ',' '\n' <<<"$WEBHOOK_EVENTS_CSV" | sed '/^$/d' | sort -u | tr '\n' ',' | sed 's/,$//'
)"
if [ "$sorted_events_joined" != "$sorted_required_events" ]; then
needs_apply_reasons+=("events-mismatch")
fi
fi

if [ "$APPLY" -eq 1 ]; then
Expand All @@ -134,6 +154,22 @@ for org in "${orgs[@]}"; do
hooks_json="$(gh api "/orgs/$org/hooks")"
hook_obj="$(jq --argjson id "$hook_id" '.[] | select(.id == $id)' <<<"$hooks_json")"
events_joined="$(jq -r '.events | join(",")' <<<"$hook_obj")"

# Re-evaluate event scope after apply mode rewrites events.
needs_apply_reasons=()
if [ "$events_joined" = "*" ]; then
needs_apply_reasons+=("wildcard-events")
else
sorted_events_joined="$(
tr ',' '\n' <<<"$events_joined" | sed '/^$/d' | sort -u | tr '\n' ',' | sed 's/,$//'
)"
sorted_required_events="$(
tr ',' '\n' <<<"$WEBHOOK_EVENTS_CSV" | sed '/^$/d' | sort -u | tr '\n' ',' | sed 's/,$//'
)"
if [ "$sorted_events_joined" != "$sorted_required_events" ]; then
needs_apply_reasons+=("events-mismatch")
fi
fi
fi

# Delivery-level verification (signature header + response code)
Expand Down
2 changes: 2 additions & 0 deletions scripts/discover-org-checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ discover_org() {
j = cfg.is_a?(Hash) ? cfg : {}
jname = j["name"].to_s
jname = jid.to_s if jname.nil? || jname.empty?
# Include both raw check-run name and workflow/job for compatibility.
puts "- #{jname}"
puts "- #{wname} / #{jname}"
end
' 2>/dev/null || true)
Expand Down
103 changes: 84 additions & 19 deletions scripts/generate-org-rulesets.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,81 @@
}

candidate_checks = [
'ChittyConnect Sync / sync',
'Compliance Check / compliance',
'CI / test',
'CI / build',
'CI / lint-and-test',
'CI / lint-and-format',
'CI / test-and-build',
'CodeQL / Analyze',
'PR Validation / validate',
'ChittyOS Compliance Check / compliance',
{
'name': 'ChittyConnect Sync / sync',
'context': 'sync',
'aliases': ['sync', 'ChittyConnect Sync / sync'],
},
{
'name': 'Compliance Check / compliance',
'context': 'compliance',
'aliases': ['compliance', 'Compliance Check / compliance'],
},
{
'name': 'CI / test',
'context': 'test',
'aliases': ['test', 'CI / test'],
},
{
'name': 'CI / build',
'context': 'build',
'aliases': ['build', 'CI / build'],
},
{
'name': 'CI / lint-and-test',
'context': 'lint-and-test',
'aliases': ['lint-and-test', 'CI / lint-and-test'],
},
{
'name': 'CI / lint-and-format',
'context': 'lint-and-format',
'aliases': ['lint-and-format', 'CI / lint-and-format'],
},
{
'name': 'CI / test-and-build',
'context': 'test-and-build',
'aliases': ['test-and-build', 'CI / test-and-build'],
},
{
'name': 'CodeQL / Analyze',
'context': 'CodeQL',
'aliases': ['CodeQL', 'CodeQL / Analyze', 'Analyze (javascript)'],
},
{
'name': 'PR Validation / validate',
'context': 'validate',
'aliases': ['validate', 'PR Validation / validate'],
},
{
'name': 'ChittyOS Compliance Check / compliance',
'context': 'compliance',
'aliases': ['compliance', 'ChittyOS Compliance Check / compliance'],
},
]
REQUIRED_APPROVING_REVIEW_COUNT = int(
os.getenv('REQUIRED_APPROVING_REVIEW_COUNT', '0')
)


def parse_required_approving_review_count():
raw = os.getenv('REQUIRED_APPROVING_REVIEW_COUNT', '0')
if raw == '':
return 0
try:
value = int(raw)
except ValueError:
sys.stderr.write(
'ERROR: REQUIRED_APPROVING_REVIEW_COUNT must be an integer between 0 and 6; '
f'got {raw!r}.\n'
)
sys.exit(1)
if value < 0 or value > 6:
sys.stderr.write(
'ERROR: REQUIRED_APPROVING_REVIEW_COUNT must be between 0 and 6; '
f'got {value}.\n'
)
sys.exit(1)
return value


REQUIRED_APPROVING_REVIEW_COUNT = parse_required_approving_review_count()


def parse_repo_checks(p: Path):
Expand Down Expand Up @@ -75,9 +136,9 @@ def baseline_ruleset(org: str):
}


def check_ruleset(check: str, repos: list[str]):
def check_ruleset(name: str, context: str, repos: list[str]):
slug = (
check.lower()
name.lower()
.replace(' / ', '-')
.replace('/', '-')
.replace(' ', '-')
Expand All @@ -88,7 +149,7 @@ def check_ruleset(check: str, repos: list[str]):
while '--' in slug:
slug = slug.replace('--', '-')
payload = {
'name': f'Require {check}',
'name': f'Require {name}',
'target': 'branch',
'enforcement': 'active',
'conditions': {
Expand All @@ -101,7 +162,7 @@ def check_ruleset(check: str, repos: list[str]):
'parameters': {
'strict_required_status_checks_policy': True,
'required_status_checks': [
{'context': check}
{'context': context}
],
},
}
Expand All @@ -128,10 +189,14 @@ def main():

files = ['00-baseline.json']
for check in candidate_checks:
repos = [r for r, checks in repo_checks.items() if check in checks]
aliases = set(check['aliases'])
repos = [
r for r, checks in repo_checks.items()
if aliases.intersection(checks)
]
if not repos:
continue
slug, payload = check_ruleset(check, repos)
slug, payload = check_ruleset(check['name'], check['context'], repos)
fname = f'10-require-{slug}.json'
(org_dir / fname).write_text(json.dumps(payload, indent=2) + '\n')
files.append(fname)
Expand Down
Loading