diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 10cd789..291df4d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ on: default: "manual" permissions: - contents: write + contents: read concurrency: group: release-${{ github.ref }} @@ -98,6 +98,8 @@ jobs: runs-on: ubuntu-latest needs: - package + permissions: + contents: write steps: - name: Download artifacts uses: actions/download-artifact@v8 diff --git a/README.md b/README.md index d7d3147..5b496f8 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ This is useful for: - [Usage guide](docs/usage.md) - [Examples](examples/README.md) - [Contributing](CONTRIBUTING.md) +- [Permission posture](docs/permissions.md) - [Testing](docs/testing.md) - [CI/CD](docs/ci.md) - [Release process](docs/release.md) diff --git a/docs/ci.md b/docs/ci.md index 0578e07..8bd745a 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -79,6 +79,10 @@ The tag version is embedded in the CLI through: prmaven version ``` +The workflow defaults to `contents: read`. Only the release publishing job receives `contents: write`. + +Repository permission posture is documented in [permissions.md](permissions.md). + ## Local Parity Before opening a PR, contributors should run: diff --git a/docs/permissions.md b/docs/permissions.md new file mode 100644 index 0000000..6e17429 --- /dev/null +++ b/docs/permissions.md @@ -0,0 +1,68 @@ +# Permission Posture + +This document records the intended repository permission model for PR Maven CLI. + +## Current Stabilization Mode + +The repository is public during the initial stabilization phase. + +This keeps GitHub branch protection available without requiring a paid plan. + +Do not switch it to private if that would disable branch protection or repository rules. A private stabilization phase should only be used when the account or organization plan supports the same branch protection controls used in public OSS mode. + +Current maintainer policy: + +- William Thomaz, GitHub: `@Will-thom`, is the founder and principal maintainer. +- No external collaborator should receive write, maintain, or admin access during stabilization unless intentionally promoted. +- External contributors can open issues and pull requests, but merge rights should stay limited to maintainers. + +## Public OSS Mode + +The public repository must keep `main` protected before accepting outside contributions. + +Required public-mode controls: + +- Require the `All CI checks` status before merge. +- Enforce the rule for administrators. +- Require conversation resolution before merge. +- Disable force pushes. +- Disable branch deletion. +- Keep merge rights limited to users with write, maintain, or admin access. +- Keep `@Will-thom` as the repository-wide code owner through `.github/CODEOWNERS`. + +## GitHub Actions Permissions + +Repository-level workflow token default: + +- `contents: read`. +- Workflows cannot approve pull request reviews. + +Workflow-specific policy: + +- `CI` uses `contents: read`. +- `Security` uses `contents: read`, `security-events: write`, and `pull-requests: read`. +- `Release` defaults to `contents: read`; only the release publishing job receives `contents: write`. +- `Thank Contributor` uses `pull_request_target` only to read the base repository template and write a comment. It must not check out or execute contributor code. + +## Secrets And External Integrations + +The project should avoid repository secrets for the Stage 1 local-first MVP. + +Expected empty surfaces during stabilization: + +- GitHub Actions secrets. +- GitHub Actions variables. +- Webhooks. +- Environments. +- Deployments. +- GitHub Pages. + +## OSS Contributor Readiness + +Before actively inviting OSS contributors: + +1. Validate branch protection for `main`. +2. Confirm `All CI checks` is the required status gate. +3. Confirm `CODEOWNERS` still routes ownership to `@Will-thom`. +4. Confirm no unintended collaborators, secrets, variables, webhooks, environments, deployments, releases, or pages were added. +5. Re-run CI and Security on `main`. diff --git a/docs_test.go b/docs_test.go index 69c41d2..db34787 100644 --- a/docs_test.go +++ b/docs_test.go @@ -10,6 +10,7 @@ func TestDocumentationCoversInstallationUsageAndExamples(t *testing.T) { files := map[string]string{ "README.md": mustReadFile(t, "README.md"), "docs/installation.md": mustReadFile(t, "docs/installation.md"), + "docs/permissions.md": mustReadFile(t, "docs/permissions.md"), "docs/usage.md": mustReadFile(t, "docs/usage.md"), "examples/README.md": mustReadFile(t, "examples/README.md"), "examples/library/main.go": mustReadFile(t, "examples/library/main.go"), @@ -18,7 +19,10 @@ func TestDocumentationCoversInstallationUsageAndExamples(t *testing.T) { assertContains(t, files["README.md"], "[Installation](docs/installation.md)") assertContains(t, files["README.md"], "[Usage guide](docs/usage.md)") assertContains(t, files["README.md"], "[Examples](examples/README.md)") + assertContains(t, files["README.md"], "[Permission posture](docs/permissions.md)") assertContains(t, files["docs/installation.md"], "go install ./cmd/prmaven") + assertContains(t, files["docs/permissions.md"], "Do not switch it to private if that would disable branch protection or repository rules.") + assertContains(t, files["docs/permissions.md"], "All CI checks") assertContains(t, files["docs/installation.md"], "prmaven version") assertContains(t, files["docs/usage.md"], "prmaven fails -project .") assertContains(t, files["docs/usage.md"], "demo/multi-module-failure")