a0af824d09
- cad_runtime.py: chmod 0o777 → 0o700 for temp directories - setup_repo.sh: replace mapfile with while-read for bash 3.2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
247 lines
7.4 KiB
Bash
Executable File
247 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# setup_repo.sh — GitHub CLI bootstrap for this template
|
|
#
|
|
# What it does:
|
|
# 1) Creates/updates all recommended labels (ai:*, type:*, scope:*, prio:*, risk:*, needs:*).
|
|
# 2) Optionally enables Discussions.
|
|
# 3) Configures branch protection on the target branch with required checks.
|
|
#
|
|
# Usage:
|
|
# bash setup_repo.sh owner/repo
|
|
#
|
|
# Env:
|
|
# BRANCH=main
|
|
# ENABLE_DISCUSSIONS=0|1
|
|
# REQUIRE_BUILD_CHECKS=0|1
|
|
# DRY_RUN=0|1
|
|
#
|
|
# Notes:
|
|
# - Requires: gh >= 2.x, authenticated (gh auth login)
|
|
# - Branch protection requires admin rights.
|
|
# -----------------------------------------------------------------------------
|
|
|
|
REPO_FULL="${1:-}"
|
|
BRANCH="${BRANCH:-main}"
|
|
ENABLE_DISCUSSIONS="${ENABLE_DISCUSSIONS:-0}"
|
|
REQUIRE_BUILD_CHECKS="${REQUIRE_BUILD_CHECKS:-1}"
|
|
DRY_RUN="${DRY_RUN:-0}"
|
|
REQUIRED_CONTEXTS_JSON="${REQUIRED_CONTEXTS_JSON:-}"
|
|
|
|
if [[ -z "${REPO_FULL}" ]]; then
|
|
echo "Usage: $0 <owner/repo>" >&2
|
|
exit 2
|
|
fi
|
|
|
|
OWNER="${REPO_FULL%/*}"
|
|
REPO="${REPO_FULL#*/}"
|
|
|
|
shell_join() {
|
|
local quoted=()
|
|
local arg
|
|
for arg in "$@"; do
|
|
quoted+=("$(printf '%q' "$arg")")
|
|
done
|
|
printf '%s' "${quoted[*]}"
|
|
}
|
|
|
|
run_cmd() {
|
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
printf '[dry-run] '
|
|
shell_join "$@"
|
|
printf '\n'
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
run_cmd_quiet() {
|
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
printf '[dry-run] '
|
|
shell_join "$@"
|
|
printf '\n'
|
|
else
|
|
"$@" >/dev/null
|
|
fi
|
|
}
|
|
|
|
need_gh() {
|
|
command -v gh >/dev/null 2>&1 || { echo "gh not found. Install GitHub CLI first." >&2; exit 1; }
|
|
gh auth status -h github.com >/dev/null 2>&1 || { echo "Not authenticated. Run: gh auth login" >&2; exit 1; }
|
|
}
|
|
|
|
create_label() {
|
|
local name="$1"; local color="$2"; local desc="$3"
|
|
run_cmd_quiet gh label create "${name}" -R "${REPO_FULL}" --color "${color}" --description "${desc}" --force
|
|
}
|
|
|
|
setup_labels() {
|
|
echo "==> Creating/updating labels in ${REPO_FULL} ..."
|
|
|
|
# ai:*
|
|
create_label "ai:spec" "0E8A16" "Agent scope: generate/normalize specification (RFC2119)."
|
|
create_label "ai:plan" "1D76DB" "Agent scope: architecture/plan/ADR/test strategy."
|
|
create_label "ai:tasks" "5319E7" "Agent scope: task breakdown, checklists, backlog."
|
|
create_label "ai:impl" "FBCA04" "Agent scope: implementation within allowed directories."
|
|
create_label "ai:qa" "D93F0B" "Agent scope: tests, edge cases, validation, hardening."
|
|
create_label "ai:docs" "0B4F6C" "Agent scope: documentation only."
|
|
create_label "ai:hold" "B60205" "STOP automation. Human review required."
|
|
|
|
# type:*
|
|
create_label "type:feature" "C2E0C6" "Feature request."
|
|
create_label "type:bug" "B60205" "Bug report."
|
|
create_label "type:consulting" "D4C5F9" "Consulting-style intake/cadrage."
|
|
create_label "type:systems" "A2EEEF" "Systems/engineering/architecture."
|
|
create_label "type:design" "F9D0C4" "UX/UI/industrial design."
|
|
create_label "type:creative" "FAD8C7" "Creative/narrative/content production."
|
|
create_label "type:spike" "FEF2C0" "R&D spike, time-boxed."
|
|
create_label "type:compliance" "0052CC" "Compliance / release readiness."
|
|
create_label "type:agentics" "6F42C1" "Agentic workflow updates."
|
|
|
|
# scope:*
|
|
create_label "scope:firmware" "1F6FEB" "Firmware scope."
|
|
create_label "scope:hardware" "0E8A16" "Hardware scope."
|
|
create_label "scope:docs" "0B4F6C" "Docs scope."
|
|
create_label "scope:ux" "FBCA04" "UX/UI scope."
|
|
create_label "scope:content" "FAD8C7" "Creative content scope."
|
|
create_label "scope:infra" "5319E7" "CI/CD & repo infrastructure scope."
|
|
|
|
# risk:*
|
|
create_label "risk:low" "C2E0C6" "Low risk."
|
|
create_label "risk:med" "FBCA04" "Medium risk."
|
|
create_label "risk:high" "B60205" "High risk."
|
|
|
|
# prio:*
|
|
create_label "prio:p0" "B60205" "P0 urgent."
|
|
create_label "prio:p1" "D93F0B" "P1 high."
|
|
create_label "prio:p2" "FBCA04" "P2 normal."
|
|
create_label "prio:p3" "C2E0C6" "P3 low."
|
|
|
|
# needs:*
|
|
create_label "needs:triage" "EDEDED" "Needs triage."
|
|
create_label "needs:decision" "EDEDED" "Needs decision / arbitration."
|
|
create_label "needs:review" "EDEDED" "Needs review."
|
|
create_label "needs:assets" "EDEDED" "Needs assets (design/content)."
|
|
|
|
echo "==> Labels done."
|
|
}
|
|
|
|
enable_discussions() {
|
|
if [[ "${ENABLE_DISCUSSIONS}" != "1" ]]; then
|
|
echo "==> Discussions: skipped (ENABLE_DISCUSSIONS=0)."
|
|
return 0
|
|
fi
|
|
|
|
echo "==> Enabling Discussions on ${REPO_FULL} ..."
|
|
|
|
local repo_id
|
|
repo_id="$(gh api graphql -f owner="${OWNER}" -f name="${REPO}" -f query='
|
|
query($owner:String!, $name:String!) {
|
|
repository(owner:$owner, name:$name) { id hasDiscussionsEnabled }
|
|
}' --jq '.data.repository.id')"
|
|
|
|
local mutation='mutation($repositoryId:ID!, $enabled:Boolean!) {
|
|
updateRepository(input:{repositoryId:$repositoryId, hasDiscussionsEnabled:$enabled}) {
|
|
repository { name hasDiscussionsEnabled }
|
|
}
|
|
}'
|
|
run_cmd_quiet gh api graphql -f repositoryId="${repo_id}" -F enabled=true -f query="${mutation}"
|
|
|
|
echo "==> Discussions enabled."
|
|
}
|
|
|
|
load_required_contexts() {
|
|
if [[ -n "${REQUIRED_CONTEXTS_JSON}" ]]; then
|
|
python3 - "${REQUIRED_CONTEXTS_JSON}" <<'PY'
|
|
import json
|
|
import sys
|
|
|
|
payload = json.loads(sys.argv[1])
|
|
if not isinstance(payload, list) or not all(isinstance(item, str) and item.strip() for item in payload):
|
|
raise SystemExit("REQUIRED_CONTEXTS_JSON must be a JSON array of non-empty strings")
|
|
for item in payload:
|
|
print(item)
|
|
PY
|
|
return 0
|
|
fi
|
|
|
|
printf '%s\n' \
|
|
"Badges & Coverage / badges" \
|
|
"Secret Scanning / secret_scan" \
|
|
"Repo State / repo-state"
|
|
|
|
if [[ "${REQUIRE_BUILD_CHECKS}" == "1" ]]; then
|
|
printf '%s\n' \
|
|
"API Contract & Integration Testing / api_contract" \
|
|
"Evidence Pack Validation / evidence_pack"
|
|
fi
|
|
}
|
|
|
|
setup_branch_protection() {
|
|
echo "==> Configuring branch protection for ${REPO_FULL}:${BRANCH} ..."
|
|
|
|
# Required check contexts (GitHub Actions job names):
|
|
# These MUST match the check names you see in PR -> Checks.
|
|
# If you change workflow/job names, update this list.
|
|
local -a contexts=()
|
|
while IFS= read -r line; do
|
|
contexts+=("${line}")
|
|
done < <(load_required_contexts)
|
|
|
|
# JSON array for contexts
|
|
local contexts_json
|
|
contexts_json="$(printf '%s\n' "${contexts[@]}" | python3 -c 'import json, sys; print(json.dumps([line.rstrip("\n") for line in sys.stdin if line.strip()]))')"
|
|
|
|
local body
|
|
body="$(cat <<JSON
|
|
{
|
|
"required_status_checks": {
|
|
"strict": true,
|
|
"contexts": ${contexts_json}
|
|
},
|
|
"enforce_admins": true,
|
|
"required_pull_request_reviews": {
|
|
"dismiss_stale_reviews": true,
|
|
"require_code_owner_reviews": false,
|
|
"required_approving_review_count": 1
|
|
},
|
|
"restrictions": null,
|
|
"required_linear_history": true,
|
|
"allow_force_pushes": false,
|
|
"allow_deletions": false,
|
|
"required_conversation_resolution": true
|
|
}
|
|
JSON
|
|
)"
|
|
|
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
echo "[dry-run] Would PUT /repos/${OWNER}/${REPO}/branches/${BRANCH}/protection with body:"
|
|
echo "${body}"
|
|
return 0
|
|
fi
|
|
|
|
gh api \
|
|
--method PUT \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"/repos/${OWNER}/${REPO}/branches/${BRANCH}/protection" \
|
|
--input - <<<"${body}" >/dev/null
|
|
|
|
echo "==> Branch protection enabled."
|
|
echo " Required checks:"
|
|
printf ' - %s\n' "${contexts[@]}"
|
|
}
|
|
|
|
main() {
|
|
if [[ "${DRY_RUN}" != "1" ]]; then
|
|
need_gh
|
|
fi
|
|
setup_labels
|
|
enable_discussions
|
|
setup_branch_protection
|
|
echo "✅ Done."
|
|
}
|
|
|
|
main
|