Skip to content

fix(rules): keep the public toggle on repository visibility rules - #4455

Open
yordis wants to merge 1 commit into
google:masterfrom
yordis:yordis/fix-repository-visibility-public
Open

fix(rules): keep the public toggle on repository visibility rules#4455
yordis wants to merge 1 commit into
google:masterfrom
yordis:yordis/fix-repository-visibility-public

Conversation

@yordis

@yordis yordis commented Aug 17, 2026

Copy link
Copy Markdown

RepositoryVisibilityRuleParameters models only internal and private, but the repository policy API has a third toggle, public. Because encoding/json discards unknown keys, the flag is lost on read, and since PUT /orgs/{org}/rulesets/{ruleset_id} is a full replace, writing back a ruleset the SDK just read silently resets public visibility to false.

Found while adding the repository ruleset target to the Terraform provider: integrations/terraform-provider-github#3598.

Verified against the live API

Against a real target: repository ruleset on a github.com organization on the Team plan, using a program that fetches the same ruleset twice: once over plain net/http to capture the wire bytes, and once through Organizations.GetRepositoryRuleset. Org name and ruleset ID redacted.

On v90.0.0:

1. raw HTTP body      {"public":false,"internal":true,"private":true}
2. after SDK decode   {"internal":true,"private":true}
3. struct value       {Internal:true Private:true}

The server sends the key and the SDK drops it. Since UpdateRepositoryRuleset is a full replace, a read-modify-write built from that value omits public and the API resolves it to false.

With this patch applied:

1. raw HTTP body      {"public":false,"internal":true,"private":true}
2. after SDK decode   {"internal":true,"private":true,"public":false}
3. struct value       {Internal:true Private:true Public:false}

Then a write round trip through Organizations.UpdateRepositoryRuleset, toggling only Public on the decoded struct:

--- write phase, original public=false ---
PUT public=true  -> raw GET {"public":true,"internal":true,"private":true}
PUT public=false -> raw GET {"public":false,"internal":true,"private":true}

The API accepts the field on write and echoes it back, and the "Restrict visibility" section of the org policy UI showed Public checked while it was true. After restoring, the full ruleset body diffed equal to a backup taken before the run, ignoring updated_at.

Verified in this repo

  • go build ./...
  • go test ./... -count=1
  • script/fmt.sh and script/lint.sh, including the generated-files check

github-accessors.go and github-accessors_test.go are regenerated output.

Test coverage added for the round trip: the repository_visibility cases in TestRepositoryRule now carry public: true in both the marshal and unmarshal tables, which fails without the struct field.

For context on why this was missed: repository_visibility does not appear anywhere in github/rest-api-description (zero matches in both api.github.com.json and ghec.json as of today), so there is no generated schema to check the field list against. The name and JSON shape used here come from the live responses above.

GitHub's repository policy API exposes a third visibility toggle, and dropping it on unmarshal silently revokes public visibility on any full-replace update built from a ruleset the SDK read back.

Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@alexandear

Copy link
Copy Markdown
Contributor

@yordis

yordis commented Aug 17, 2026

Copy link
Copy Markdown
Author

@alexandear what about it?

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.

2 participants