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
58 changes: 58 additions & 0 deletions .github/rulesets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Branch rulesets

Each JSON file here is a GitHub branch ruleset exported from the template's
canonical configuration. Apply them to a new repo (after creating it from
the template) with:

```sh
.github/scripts/apply-rulesets.sh # current repo
.github/scripts/apply-rulesets.sh owner/repo # explicit target
```

The script is idempotent — re-running it updates a ruleset in place when
one with the same `name` already exists, rather than creating a duplicate.

Requirements:

- `gh` CLI authenticated as a repo admin (org or user). Branch rulesets
require admin scope to create.
- `jq` available on PATH.

## What's enforced (`main.json`)

Applies to the default branch:

- **Required PR before merging** — no direct pushes to `main`. Note that
`required_approving_review_count` is `0`: a PR is required, but **zero
approvals** are needed, so authors can self-merge. This enforces process
(PR + status checks) but not peer review. Raise this value if you want to
require approvals before merge.
- **Required status checks** (not strict — branch does not need to be up
to date): Spellcheck, check-chars, build-deploy. The `build-deploy`
context is produced by `preview.yml` on PRs (publish.yml only runs on
push-to-main, so it can't satisfy this); if you rename either job, the
ruleset gate will hang. `check-links.yml` is intentionally excluded — it checks external URLs,
which can fail due to transient network issues or link rot unrelated to
the PR. Requiring it as a merge gate would block merges on external
failures outside the PR author's control.
- **No force-pushes, no branch deletion.**
- **Bypass** in `pull_request` mode for the Maintain role (role id 2) —
Maintainers can merge via a PR they authored, but cannot push directly.

## Editing the ruleset

Edit `main.json` here, then run `apply-rulesets.sh` to push the change to
the live repo. Or edit in the GitHub UI (Settings → Rules → Rulesets) and
re-export with:

```sh
# Find the ruleset ID:
RULESET_ID=$(gh api repos/OWNER/REPO/rulesets | jq '.[] | select(.name == "main") | .id')

gh api "repos/OWNER/REPO/rulesets/$RULESET_ID" \
| jq 'del(.id, .node_id, .source, .source_type, .created_at, .updated_at, ._links, .current_user_can_bypass)' \
> .github/rulesets/main.json
```

The fields stripped by `jq del(...)` are server-assigned and would either
be ignored or rejected by the create/update endpoints.
65 changes: 65 additions & 0 deletions .github/rulesets/main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "main",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"exclude": [],
"include": [
"~DEFAULT_BRANCH"
]
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 0,
"dismiss_stale_reviews_on_push": false,
"required_reviewers": [],
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_review_thread_resolution": false,
"allowed_merge_methods": [
"merge",
"squash",
"rebase"
]
}
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": false,
"do_not_enforce_on_create": false,
"required_status_checks": [
{
"context": "Spellcheck",
"integration_id": 15368
},
Comment thread
d-morrison marked this conversation as resolved.
{
"context": "check-chars",
"integration_id": 15368
},
{
"context": "build-deploy",
"integration_id": 15368
}
]
}
}
],
"bypass_actors": [
{
"actor_id": 2,
"actor_type": "RepositoryRole",
"bypass_mode": "pull_request"
}
]
}
46 changes: 46 additions & 0 deletions .github/scripts/apply-rulesets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# Apply branch rulesets from .github/rulesets/*.json to the current repo.
# Run once after creating a new repo from this template. Idempotent: a
# ruleset whose `name` already exists on the repo is updated in place rather
# than duplicated.
#
# Requires: gh CLI authenticated with admin access to the repo.
# Usage: .github/scripts/apply-rulesets.sh [owner/repo]
# Defaults to the gh-detected current repo.

set -euo pipefail

command -v jq >/dev/null 2>&1 || { echo "error: jq is required but not found" >&2; exit 1; }
Comment thread
d-morrison marked this conversation as resolved.
command -v gh >/dev/null 2>&1 || { echo "error: gh is required but not found" >&2; exit 1; }

repo="${1:-$(gh repo view --json nameWithOwner -q .nameWithOwner)}"
ruleset_dir="$(cd "$(dirname "$0")/../rulesets" && pwd)"

shopt -s nullglob
files=("$ruleset_dir"/*.json)
if [ ${#files[@]} -eq 0 ]; then
echo "no rulesets found in $ruleset_dir" >&2
exit 0
fi

# Map of existing ruleset name -> id, so we can PUT updates instead of
# creating duplicates.
existing=$(gh api --paginate "repos/$repo/rulesets" | jq -s '[.[][] | {name, id}]')

for f in "${files[@]}"; do
name=$(jq -r .name "$f")
if [ -z "$name" ] || [ "$name" = "null" ]; then
echo "skipping $f: missing .name field" >&2
continue
fi
id=$(jq -r --arg n "$name" 'map(select(.name == $n)) | .[0].id // empty' <<<"$existing")
if [ -n "$id" ]; then
echo "updating ruleset '$name' (id $id) on $repo"
gh api -X PUT "repos/$repo/rulesets/$id" --input "$f" >/dev/null
else
echo "creating ruleset '$name' on $repo"
gh api -X POST "repos/$repo/rulesets" --input "$f" >/dev/null
fi
done

echo "done."
17 changes: 14 additions & 3 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,27 @@ This template includes a GitHub Actions workflow (`.github/workflows/publish.yml
- Go to Settings → Pages
- Under "Build and deployment", set Source to "GitHub Actions"

2. **Push to main branch**:
2. **Apply branch rulesets** (requires admin access):
```bash
.github/scripts/apply-rulesets.sh
```
This protects `main` against direct pushes / force-pushes / deletion,
requires a PR to merge, and gates the merge on the configured CI checks
(Spellcheck, check-chars, build-deploy). A PR is required but **zero
approvals** are needed, so authors can self-merge; raise
`required_approving_review_count` in `.github/rulesets/main.json` to require approvals. See
`.github/rulesets/README.md` for details.
Comment thread
Copilot marked this conversation as resolved.

3. **Push to main branch**:
```bash
git add .
git commit -m "Initial book setup"
git push origin main
```

3. **Wait for the workflow** to complete (check the Actions tab)
4. **Wait for the workflow** to complete (check the Actions tab)

4. **Access your book** at: `https://YOUR-USERNAME.github.io/YOUR-REPO/`
5. **Access your book** at: `https://YOUR-USERNAME.github.io/YOUR-REPO/`

## GitHub Actions Workflows

Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,31 @@ publishes your book to GitHub Pages when you push to the main branch.
- Go to Settings → Pages
- Under “Build and deployment”, set Source to “GitHub Actions”

2. **Push to main branch**:
2. **Apply branch rulesets** (requires admin access):

``` bash
.github/scripts/apply-rulesets.sh
```

This protects `main` against direct pushes / force-pushes /
deletion, requires a PR to merge, and gates the merge on the
configured CI checks (Spellcheck, check-chars, build-deploy). A PR
is required but **zero approvals** are needed, so authors can
self-merge; raise `required_approving_review_count` in
`.github/rulesets/main.json` to require approvals. See
`.github/rulesets/README.md` for details.

3. **Push to main branch**:

``` bash
git add .
git commit -m "Initial book setup"
git push origin main
```

3. **Wait for the workflow** to complete (check the Actions tab)
4. **Wait for the workflow** to complete (check the Actions tab)

4. **Access your book** at:
5. **Access your book** at:
`https://YOUR-USERNAME.github.io/YOUR-REPO/`

## GitHub Actions Workflows
Expand Down
1 change: 1 addition & 0 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ emplate
glitchy
lintr
ook
rulesets
uarto
Loading