Skip to content

feat: add shared repository ruleset governance - #1

Merged
vitormattos merged 88 commits into
mainfrom
feat/safe-settings-governance
Sep 19, 2026
Merged

vitormattos merged 88 commits into
mainfrom
feat/safe-settings-governance

Conversation

@vitormattos

@vitormattos vitormattos commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Summary

Introduces the first shared governance implementation for LibreCodeCoop and LibreSign.

This PR intentionally does not modify production rulesets yet. It establishes a testable core, caller-owned configuration, and dry-run paths first.

Architecture

  • shared implementation lives in this repository
  • organization repositories keep only organization-specific configuration and credentials
  • repository-scoped execution supports short-lived GitHub App tokens limited to one repository
  • shared policy bodies are selected by name from caller config
  • exceptional repository-only rulesets do not require hardcoding repository names in the engine

Ruleset behavior

  • base policy matches the current LibreSign default/stable branch protection
  • public, non-archived repositories only
  • automatic Nextcloud app detection through appinfo/info.xml
  • pinned nextcloud-bot bypass for Nextcloud apps
  • fail-closed behavior for unexpected GitHub API errors
  • deterministic normalization and drift detection
  • create/update reconciliation without deleting unrelated rulesets
  • organization-wide and repository-scoped dry-run
  • mutation only with explicit --apply

Quality baseline

The repository follows the same licensing and supply-chain expectations used by LibreSign:

  • AGPL-3.0-or-later
  • SPDX headers on source, tests, workflows, and documentation
  • REUSE.toml for formats where inline SPDX comments are inappropriate
  • canonical license text under LICENSES/ plus top-level LICENSE
  • GitHub Actions pinned to commit SHAs
  • read-only workflow permissions by default
  • separate workflows so each check has one clear responsibility:
    • actionlint.yml
    • vitest.yml
    • typescript.yml
    • reuse.yml
    • zizmor.yml

Current checks are green: actionlint, Vitest, TypeScript/build, REUSE, and zizmor.

The README is intentionally short and focused on project value and principles. Technical architecture, development instructions, and the Safe Settings evaluation live under docs/.

Safe Settings evaluation

Safe Settings was evaluated before implementing reconciliation here.

Its internal rulesets plugin already supports repository ruleset REST endpoints, but the current supported configuration flow treats rulesets as organization-level settings: repo/suborg schemas do not expose them and Settings.returnRepoSpecificConfigs() removes rulesets before repository configuration is applied.

That does not cover our GitHub Free use case, where public repository rulesets are available but organization-wide rulesets require a paid plan.

The implementation here therefore owns only repository-ruleset reconciliation. Safe Settings can still be used later for settings it supports well, and repository-level ruleset support is a candidate upstream contribution.

See docs/safe-settings.md for details.

Current-state validation

The implementation was compared with live LibreSign rulesets:

  • LibreSign/libresign currently has the expected nextcloud-bot bypass
  • LibreSign/documentation currently has only the base bypass
  • LibreSign/.github currently exposes only the base ruleset on GitHub, although its existing governance repository contains an additional CI ruleset definition; migration must distinguish desired config from currently applied state

Migration plan

  1. keep this PR in dry-run-only status
  2. add the reusable organization workflow/action wrapper
  3. run repository-scoped dry-run against LibreSign using the existing GitHub App credential model
  4. compare output with the existing sync-rulesets.sh
  5. migrate LibreSign and LibreCodeCoop .github repositories to caller config + shared implementation
  6. remove the ruleset engine from LibreSign/.github only after parity is proven

Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
Signed-off-by: LibreSign automation <noreply@libresign.coop>
@vitormattos
vitormattos marked this pull request as ready for review September 19, 2026 21:04
@vitormattos
vitormattos merged commit 7ccfdf0 into main Sep 19, 2026
5 of 6 checks passed
@vitormattos
vitormattos deleted the feat/safe-settings-governance branch September 19, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant