diff --git a/release_automation/docs/repository-setup.md b/release_automation/docs/repository-setup.md
index 9cdcb9c..7feba60 100644
--- a/release_automation/docs/repository-setup.md
+++ b/release_automation/docs/repository-setup.md
@@ -1,15 +1,16 @@
# Repository Setup for Release Automation
-**Last Updated**: 2026-02-13
+**Last Updated**: 2026-03-01
## Overview
API repositories that adopt the CAMARA release automation need specific repository-level configuration. The workflow manages its own labels and concurrency, but cannot self-configure branch protection, CODEOWNERS entries, or install the caller workflow.
-This document defines the required configuration for each API repository. It serves as:
-- **Setup guide** for repository administrators onboarding new or existing repos
-- **Verification reference** for test repo setup (WS9 testing phase)
-- **Input specification** for the onboarding campaign and admin tooling
+This document defines the required configuration for each API repository. It serves as the **specification** that the automated onboarding tooling implements — repository administrators do not need to apply or verify this configuration manually.
+
+**Automated application**: The [release automation onboarding campaign](https://github.com/camaraproject/project-administration/pull/134) in `project-administration` applies the full configuration to API repositories. It includes a campaign workflow (caller workflow, CHANGELOG structure, CODEOWNERS adjustments) and an admin script (repository ruleset). Both support dry-run/plan modes and phased rollout — test repositories first, then volunteering repos, then all.
+
+**New repositories**: After rollout, the configuration will also be applied to `Template_API_Repository` ([camaraproject/tooling#82](https://github.com/camaraproject/tooling/issues/82)), so that newly created API repositories inherit it automatically.
### What the workflow manages internally
@@ -23,8 +24,8 @@ This document defines the required configuration for each API repository. It ser
| Item | Purpose | Section |
|------|---------|---------|
-| 3 repository rulesets | Branch protection for automation-managed branches | [Rulesets](#repository-rulesets) |
-| CODEOWNERS entry | RM reviewer assignment for Release PRs | [CODEOWNERS](#codeowners-requirements) |
+| Repository ruleset | Branch protection for snapshot branches | [Ruleset](#repository-ruleset) |
+| CODEOWNERS file | Codeowner assignment for `/publish-release` authorization | [CODEOWNERS](#codeowners-requirements) |
| Caller workflow file | Entry point that connects the repo to the automation | [Caller Workflow](#caller-workflow) |
| `release-plan.yaml` | Release configuration (target tag, type, APIs) | [Required Files](#required-files) |
| README delimiters | Release Information section markers | [Required Files](#required-files) |
@@ -34,24 +35,41 @@ This document defines the required configuration for each API repository. It ser
## Repository Rulesets
-Three rulesets protect the branches that the release automation creates and manages. All three use **GitHub Actions** as a bypass actor to allow the workflow's `GITHUB_TOKEN` to operate while blocking direct human modifications.
+Three rulesets protect branches managed by the release automation:
+
+1. **Snapshot branch protection** — protects `release-snapshot/**` branches with branch protection rules and PR review requirements
+2. **Release pointer branch protection** — protects `release/**` pointer branches (fully immutable)
+3. **Pre-release pointer branch protection** — protects `pre-release/**` pointer branches (immutable but deletable by codeowners)
-### 1. Snapshot Branch Protection
+The `camara-release-automation` GitHub App is the bypass actor for all rulesets, allowing the workflow to create and manage these branches while humans are governed by protection rules.
-Prevents human modification of snapshot branches. The workflow pushes mechanically generated content (transformed API files, release-metadata.yaml) to these branches — any human modification would compromise release integrity.
+No ruleset is needed for `release-review/**` branches — codeowners push review fixes directly to these branches, and the workflow handles creation and cleanup.
+
+### Snapshot Branch Protection
| Property | Value |
|----------|-------|
| **Name** | `release-snapshot-protection` |
| **Enforcement** | Active |
| **Target** | Include branches matching: `release-snapshot/**` |
-| **Bypass actors** | GitHub Actions (always) |
+| **Bypass actors** | `camara-release-automation` GitHub App (always), Organization admins (always) |
-**Rules:**
-- Restrict pushes — only bypass actors may push
+**Branch protection rules:**
+- Restrict creations — only bypass actors may create snapshot branches
- Restrict deletions — only bypass actors may delete
- Block force pushes
+**PR review rules:**
+- Require a pull request before merging
+- Required approvals: 2 (ensures two distinct people must approve, even if a person is in both codeowner and RM reviewer teams)
+- Require review from Code Owners
+- Dismiss stale reviews on new pushes
+- Required reviewers: `release-management_reviewers` team (1 approval, all files)
+
+The dual review gate ensures both API codeowners and Release Management reviewers must approve before a Release PR can be merged:
+- The `*` CODEOWNERS pattern assigns API codeowners as reviewers
+- The ruleset's `required_reviewers` field auto-requests the `release-management_reviewers` team
+
GitHub API payload for programmatic application
@@ -67,18 +85,39 @@ Prevents human modification of snapshot branches. The workflow pushes mechanical
}
},
"rules": [
- { "type": "non_fast_forward" },
{ "type": "deletion" },
+ { "type": "non_fast_forward" },
+ { "type": "creation" },
{
- "type": "push",
+ "type": "pull_request",
"parameters": {
- "restrict_pushes": true
+ "required_approving_review_count": 2,
+ "dismiss_stale_reviews_on_push": true,
+ "required_reviewers": [
+ {
+ "minimum_approvals": 1,
+ "file_patterns": ["*"],
+ "reviewer": {
+ "id": 13109132,
+ "type": "Team"
+ }
+ }
+ ],
+ "require_code_owner_review": true,
+ "require_last_push_approval": false,
+ "required_review_thread_resolution": false,
+ "allowed_merge_methods": ["merge", "squash", "rebase"]
}
}
],
"bypass_actors": [
{
- "actor_id": 2,
+ "actor_id": null,
+ "actor_type": "OrganizationAdmin",
+ "bypass_mode": "always"
+ },
+ {
+ "actor_id": 2865881,
"actor_type": "Integration",
"bypass_mode": "always"
}
@@ -86,48 +125,70 @@ Prevents human modification of snapshot branches. The workflow pushes mechanical
}
```
-Note: `actor_id: 2` refers to the GitHub Actions app. Verify the correct ID for your organization via `GET /orgs/{org}/rulesets` on an existing ruleset that uses GitHub Actions bypass.
+Notes:
+- `actor_id: 2865881` is the `camara-release-automation` GitHub App ID
+- `reviewer.id: 13109132` is the `release-management_reviewers` team ID
+- The `required_reviewers` field is a beta feature in the GitHub Rulesets API
+- The canonical ruleset is maintained in `Template_API_Repository` — the JSON above matches it
-### 2. Release-Review Branch Protection
+### Release Pointer Branch Protection
+
+After publication, the automation creates a pointer branch at the release tag commit (`release/rX.Y` for public releases, `pre-release/rX.Y` for pre-releases). This prevents GitHub's "commit does not belong to any branch" warning when browsing the tag tree view.
+
+Two rulesets enforce immutability (no commits, no force pushes). They differ only in deletion policy:
-Prevents deletion of release-review branches while allowing codeowners to push review fixes. The release-review branch is the PR head where codeowners may address review comments directly.
+**`release-pointer-protection`** — public release pointers are fully protected:
| Property | Value |
|----------|-------|
-| **Name** | `release-review-protection` |
+| **Name** | `release-pointer-protection` |
| **Enforcement** | Active |
-| **Target** | Include branches matching: `release-review/**` |
-| **Bypass actors** | GitHub Actions (always) |
+| **Target** | Include branches matching: `release/**` |
+| **Bypass actors** | `camara-release-automation` GitHub App (always), Organization admins (always) |
-**Rules:**
-- Restrict deletions — only bypass actors may delete (workflow cleans up after publication)
-- Block force pushes
+Rules: restrict creations, restrict deletions, restrict updates, block force pushes. No PR review rules.
+
+**`pre-release-pointer-protection`** — pre-release pointers are immutable but deletable:
-Note: No push restriction — codeowners may push directly to fix review comments on the Release PR.
+| Property | Value |
+|----------|-------|
+| **Name** | `pre-release-pointer-protection` |
+| **Enforcement** | Active |
+| **Target** | Include branches matching: `pre-release/**` |
+| **Bypass actors** | `camara-release-automation` GitHub App (always), Organization admins (always) |
+
+Rules: restrict creations, restrict updates, block force pushes. **No deletion rule** — codeowners can delete older pre-release pointers to manage the branch list as pre-releases accumulate during a release cycle.
-GitHub API payload
+GitHub API payloads
```json
{
- "name": "release-review-protection",
+ "name": "release-pointer-protection",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
- "include": ["refs/heads/release-review/**"],
+ "include": ["refs/heads/release/**"],
"exclude": []
}
},
"rules": [
- { "type": "non_fast_forward" },
- { "type": "deletion" }
+ { "type": "creation" },
+ { "type": "deletion" },
+ { "type": "update" },
+ { "type": "non_fast_forward" }
],
"bypass_actors": [
{
- "actor_id": 2,
+ "actor_id": null,
+ "actor_type": "OrganizationAdmin",
+ "bypass_mode": "always"
+ },
+ {
+ "actor_id": 2865881,
"actor_type": "Integration",
"bypass_mode": "always"
}
@@ -135,80 +196,62 @@ Note: No push restriction — codeowners may push directly to fix review comment
}
```
-
-
-### 3. Release PR Approval Requirements
-
-Enforces review gates on pull requests that target snapshot branches. The Release PR (head: `release-review/*`, base: `release-snapshot/*`) is the human approval gate before draft release creation.
-
-| Property | Value |
-|----------|-------|
-| **Name** | `release-snapshot-pr-rules` |
-| **Enforcement** | Active |
-| **Target** | Include branches matching: `release-snapshot/**` |
-
-**Rules:**
-- Require pull request before merging
-- Required approvals: 1 (minimum)
-- Require review from Code Owners
-- Dismiss stale reviews on new pushes
-
-The dual review gate is enforced through CODEOWNERS file patterns:
-- The `*` pattern assigns all codeowners as reviewers
-- The `/CHANGELOG/` pattern assigns `@camaraproject/release-management_reviewers`
-
-Both groups must approve before the PR can be merged.
-
-
-GitHub API payload
-
```json
{
- "name": "release-snapshot-pr-rules",
+ "name": "pre-release-pointer-protection",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
- "include": ["refs/heads/release-snapshot/**"],
+ "include": ["refs/heads/pre-release/**"],
"exclude": []
}
},
"rules": [
+ { "type": "creation" },
+ { "type": "update" },
+ { "type": "non_fast_forward" }
+ ],
+ "bypass_actors": [
{
- "type": "pull_request",
- "parameters": {
- "required_approving_review_count": 1,
- "dismiss_stale_reviews_on_push": true,
- "require_code_owner_review": true,
- "require_last_push_approval": false,
- "required_review_thread_resolution": false
- }
+ "actor_id": null,
+ "actor_type": "OrganizationAdmin",
+ "bypass_mode": "always"
+ },
+ {
+ "actor_id": 2865881,
+ "actor_type": "Integration",
+ "bypass_mode": "always"
}
]
}
```
+Notes:
+- `actor_id: 2865881` is the `camara-release-automation` GitHub App ID
+- The `update` rule prevents any commits to pointer branches — they must stay at the tag commit
+
### Applying rulesets programmatically
-The GitHub Rulesets API is **not idempotent** — calling `POST` twice creates duplicate rulesets. Use the following pattern for safe re-application:
+The GitHub Rulesets API is **not idempotent** — calling `POST` twice creates duplicate rulesets. The admin script in `project-administration` uses a check-then-create/update pattern:
```bash
-# 1. List existing rulesets
+# List existing rulesets
existing=$(gh api repos/{owner}/{repo}/rulesets --jq '.[].name')
-# 2. For each target ruleset, check if it exists
+# Check if it exists, then create or update
if echo "$existing" | grep -q "release-snapshot-protection"; then
- # Update existing (get ID first)
id=$(gh api repos/{owner}/{repo}/rulesets --jq '.[] | select(.name == "release-snapshot-protection") | .id')
gh api -X PUT repos/{owner}/{repo}/rulesets/$id --input payload.json
else
- # Create new
gh api -X POST repos/{owner}/{repo}/rulesets --input payload.json
fi
```
+See `project-administration/scripts/apply-release-rulesets.sh` for the full script.
+
---
## CODEOWNERS Requirements
@@ -224,21 +267,15 @@ The release automation expects the standard CODEOWNERS format used across CAMARA
# Admin-managed files
/CODEOWNERS @camaraproject/admins
/MAINTAINERS.MD @camaraproject/admins
-
-# Release Management reviewers for changelog files
-/CHANGELOG.md @camaraproject/release-management_reviewers
-/CHANGELOG.MD @camaraproject/release-management_reviewers
```
-### Required addition for release automation
+### CODEOWNERS and RM reviewer assignment
-Add the following line to enable RM reviewer assignment for the new per-cycle changelog directory:
+CAMARA repositories have `/CHANGELOG.md` and `/CHANGELOG.MD` lines in CODEOWNERS that assign `@camaraproject/release-management_reviewers` as reviewers for the legacy root changelog file. These lines are **kept** during onboarding:
-```
-/CHANGELOG/ @camaraproject/release-management_reviewers
-```
-
-The release automation creates per-cycle changelog files at `CHANGELOG/CHANGELOG-rX.md` (e.g., `CHANGELOG/CHANGELOG-r4.md` for release cycle 4). Without this CODEOWNERS entry, Release Management reviewers would not be auto-assigned to Release PRs that modify these files.
+- They prevent codeowners from making unreviewed changes to the legacy `CHANGELOG.md` during Phase 1 (migration period), encouraging use of the new `CHANGELOG/` directory structure instead
+- RM reviewer assignment for Release PRs on snapshot branches is additionally enforced via the ruleset's `required_reviewers` field, which auto-requests the team and blocks merge until they approve
+- The `*` CODEOWNERS pattern ensures API codeowners review all files
### How CODEOWNERS is used by the automation
@@ -257,7 +294,6 @@ The `/publish-release` command checks CODEOWNERS to authorize the publishing use
- GitHub's CODEOWNERS uses "last matching pattern wins" for reviewer assignment. The automation's publish authorization currently takes the first `*` line. In standard CAMARA repositories there is only one `*` line, so this is equivalent. Repositories should not have multiple `*` lines.
- Team references (e.g., `@camaraproject/team`) on the `*` line are extracted but do not match individual usernames — use individual `@username` entries on the `*` line
- If the CODEOWNERS file does not exist (404), the check is skipped and only repository permission is verified
-- Pattern-specific rules (e.g., `/CHANGELOG/`) do not affect `/publish-release` authorization — they only affect PR review assignment
---
@@ -310,18 +346,19 @@ Minimum required fields:
```yaml
repository:
+ release_track: meta-release # or: independent
+ meta_release: Sync26 # required when release_track is meta-release
target_release_tag: r1.1
target_release_type: pre-release-rc # or: pre-release-alpha, public-release, none
- release_track: meta-release # independent or meta-release
- meta_release: Sync26
-
-apis:
- - api_name: quality-on-demand
- api_version: 0.11.0
dependencies:
- commonalities: "0.5" # Commonalities version
- identity-and-consent-management: "0.3" # ICM version (if applicable)
+ commonalities_release: r4.1
+ identity_consent_management_release: r4.1
+
+apis:
+ - api_name: release-test
+ target_api_version: 1.0.0
+ target_api_status: rc # or: draft, alpha, public
```
Valid `target_release_type` values: `pre-release-alpha`, `pre-release-rc`, `public-release`, `maintenance-release`, `none`
@@ -360,31 +397,47 @@ CHANGELOG/
Each `/create-snapshot` command generates a release section in the appropriate per-cycle file. Multiple releases within the same cycle (e.g., r4.1 alpha, r4.1 RC, r4.2) accumulate in the same file with newest entries at the top.
-### Migration from root CHANGELOG.md
+### Onboarding: CHANGELOG.md handling
+
+The onboarding campaign detects the state of the root `CHANGELOG.md` and applies the appropriate action:
+
+| Root CHANGELOG.md state | Action | CHANGELOG/README.md |
+|--------------------------|--------|---------------------|
+| **Unchanged template** (≤1 line) | Delete the placeholder | Fresh index (no historical reference) |
+| **Real content** (>1 line) | Add forward-reference note | Index with link to root CHANGELOG.md |
+| **Not present** | No action | Fresh index (no historical reference) |
+
+**Repos with unchanged template CHANGELOG.md** (e.g., ConsentManagement, eSimRemoteManagement):
-Existing CAMARA repositories have a `CHANGELOG.md` at the repository root containing historical release notes. The migration to the new directory structure happens in two phases:
+These repos were created from `Template_API_Repository` and never added real changelog entries. The single-line placeholder is deleted and `CHANGELOG/README.md` is created without a historical reference:
-**Phase 1 — Onboarding (non-breaking)**
+```markdown
+# Changelog
+
+Release changelogs are organized by release cycle.
+```
+
+**Repos with real changelog content** (most existing repos):
+
+The existing root `CHANGELOG.md` is preserved with a forward-reference note prepended:
-The onboarding campaign adds preparatory files without modifying existing content:
+```markdown
+> Starting with release automation, new release changelogs are maintained
+> in the [CHANGELOG/](CHANGELOG/) directory with per-cycle files.
+```
-1. Add a forward-reference note at the top of the existing root `CHANGELOG.md`:
- ```markdown
- > Starting with release automation, new release changelogs are maintained
- > in the [CHANGELOG/](CHANGELOG/) directory with per-cycle files.
- ```
+`CHANGELOG/README.md` includes a link back to the historical content:
-2. Create `CHANGELOG/README.md` as an index:
- ```markdown
- # Changelog
+```markdown
+# Changelog
- Release changelogs are organized by release cycle.
+Release changelogs are organized by release cycle.
- For historical release notes predating the automated release process,
- see [CHANGELOG.md](../CHANGELOG.md) in the repository root.
- ```
+For historical release notes predating the automated release process,
+see [CHANGELOG.md](../CHANGELOG.md) in the repository root.
+```
-**Phase 2 — Content migration (separate, later)**
+### Phase 2 — Content migration (separate, later)
A follow-up campaign moves the legacy content from root `CHANGELOG.md` into the `CHANGELOG/` directory. The root file is reduced to a pointer. Details of the content migration (single archive file vs. split into per-cycle files) are decided at that time.
@@ -410,22 +463,24 @@ Use this checklist to verify that a repository is correctly configured for relea
- [ ] Ruleset `release-snapshot-protection` exists and is **active**
- Target: `release-snapshot/**`
- - Rules: restrict pushes, restrict deletions, block force pushes
- - Bypass: GitHub Actions
-- [ ] Ruleset `release-review-protection` exists and is **active**
- - Target: `release-review/**`
- - Rules: restrict deletions, block force pushes
- - Bypass: GitHub Actions
-- [ ] Ruleset `release-snapshot-pr-rules` exists and is **active**
- - Target: `release-snapshot/**`
- - Rules: require PR, 1 approval, code owner review, dismiss stale reviews
+ - Branch protection: restrict creations, deletions, block force pushes
+ - PR rules: 2 approvals, code owner review, dismiss stale reviews
+ - Required reviewers: `release-management_reviewers` (1 approval)
+ - Bypass: `camara-release-automation` GitHub App
+- [ ] Ruleset `release-pointer-protection` exists and is **active**
+ - Target: `release/**`
+ - Rules: restrict creations, deletions, updates, block force pushes
+ - Bypass: `camara-release-automation` GitHub App, Organization admins
+- [ ] Ruleset `pre-release-pointer-protection` exists and is **active**
+ - Target: `pre-release/**`
+ - Rules: restrict creations, updates, block force pushes (no deletion rule)
+ - Bypass: `camara-release-automation` GitHub App, Organization admins
### CODEOWNERS
- [ ] `CODEOWNERS` file exists in repository root
- [ ] First `*` line lists at least one individual codeowner (`@username`)
-- [ ] `/CHANGELOG/` line present with `@camaraproject/release-management_reviewers`
-- [ ] `/CHANGELOG.md` and `/CHANGELOG.MD` lines present (existing CAMARA standard)
+- [ ] `/CHANGELOG.md` and/or `/CHANGELOG.MD` lines present with `@camaraproject/release-management_reviewers`
### Caller Workflow
@@ -441,8 +496,8 @@ Use this checklist to verify that a repository is correctly configured for relea
### CHANGELOG Structure
-- [ ] Root `CHANGELOG.md` has forward-reference note pointing to `CHANGELOG/` directory
- [ ] `CHANGELOG/README.md` exists as index file
+- [ ] Root `CHANGELOG.md` either: has forward-reference note (repos with history), or is deleted (repos with unchanged template placeholder)
### Smoke Test