Skip to content

feat(repo-schema): manage allow_forking with ownership-aware default#62

Merged
NWarila merged 1 commit into
mainfrom
fix/conditional-allow-forking
May 26, 2026
Merged

feat(repo-schema): manage allow_forking with ownership-aware default#62
NWarila merged 1 commit into
mainfrom
fix/conditional-allow-forking

Conversation

@NWarila
Copy link
Copy Markdown
Contributor

@NWarila NWarila commented May 26, 2026

Summary

Makes `allow_forking` an opt-in YAML key with an ownership-aware default that handles all three GitHub API behaviors correctly.

Why

GitHub's API rejects any PATCH containing `allow_forks` on personal-account private repositories — regardless of the value being set. terraform-provider-github includes the field in every PATCH payload by default, so deploys that manage personal-account private repos always failed with:

```
422 Allow forks setting can only be changed on org-owned private repositories
```

even though the framework didn't intentionally expose the field in YAML. `lifecycle.ignore_changes = [allow_forking]` only prevents diff detection — it doesn't exclude the field from the PATCH payload.

What changes

File Change
`terraform/30-locals.tf` `allow_forking` added to `allowed_repo_keys`. Build expression implements the three-way logic.
`terraform/41-resources-github.tf` Resource explicitly sets `allow_forking`. Removed from lifecycle `ignore_changes`.
`terraform/tests/validation.tftest.hcl` Removed `rejects_allow_forking` test (key is now accepted).
`terraform/tests/fixtures/bad-allow-forking/` Deleted; no longer "bad".
`terraform/tests/COVERAGE.md` G03 row repurposed.

Default policy

Scenario Default
YAML sets `allow_forking: true|false` Honor the value
YAML omits + visibility=private + `var.github_is_organization=false` `null` (provider omits field; no API rejection)
Every other case (public, internal, or org-owned private) `false` (secure-by-default; forks disabled unless YAML opts in)

Test plan

  • Framework CI green (`terraform fmt -check`, `terraform validate`, `terraform test`)
  • After merge: bump pin in `NWarila/github-terraform-runner/.github/workflows/terraform-deploy.yaml` and re-run `Deploy GitHub Terraform`; expect the 3 private repos (Resume, Personal, github-sandbox) to apply cleanly without 422.

Local: `terraform fmt -check -recursive terraform/` → clean. (Full `validate` blocked by local terraform 1.14.3 vs the pinned 1.15.4; CI exercises that.)

The GitHub API rejects any PATCH containing `allow_forks` on
personal-account private repositories — regardless of the value being
set. terraform-provider-github includes `allow_forks` in every PATCH
payload for `github_repository` resources by default, so deploys that
manage personal-account private repos always failed with 422
"Allow forks setting can only be changed on org-owned private
repositories", even though the framework didn't intentionally expose
the field in YAML.

This change makes `allow_forking` an opt-in YAML key with a default
policy that handles all three GitHub API behaviors:

  YAML explicitly sets true/false:
    Honor the value verbatim. Lets repos that need a specific fork
    policy declare it.

  YAML omits the key + visibility == "private"
    + var.github_is_organization == false:
    Default null. The provider then omits `allow_forks` from PATCH,
    bypassing the API rejection for personal-account private repos.

  Every other case (public, internal, OR org-owned private):
    Default false (secure-by-default; forks disabled unless YAML opts
    in via `allow_forking: true`). GitHub's API accepts the field on
    all three, so the framework can enforce the policy.

Concretely:
  - terraform/30-locals.tf
      `allow_forking` added to `allowed_repo_keys` (was rejected as
      unknown-top-level-key before). Build expression in
      `local.all_repositories` implements the three-way logic above.

  - terraform/41-resources-github.tf
      Resource now explicitly assigns
      `allow_forking = each.value.allow_forking`. Removed
      `allow_forking` from the lifecycle `ignore_changes` list —
      drift in YAML-managed values should surface as a real diff.

  - terraform/tests/validation.tftest.hcl
      Removed `rejects_allow_forking` test (the framework now accepts
      the key).

  - terraform/tests/fixtures/bad-allow-forking/  (deleted)
      Fixture is no longer "bad" under the new schema.

  - terraform/tests/COVERAGE.md
      G03 row repurposed to track the new positive coverage (apply-time
      test against a synthetic provider is a follow-up).

Renders correctly under both ownership values:
  - var.github_is_organization = false (personal-account):
      private repos default null → no PATCH rejection.
  - var.github_is_organization = true (org):
      private repos default false → framework enforces secure default.

No DESIGN.md or README changes; the existing prose about rejecting
unknown keys still describes the general typo-protection behavior, and
`allow_forking` is now an allowed top-level key. A follow-up doc PR
can add a paragraph about the ownership-aware default if a maintainer
finds the YAML schema needs it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Terraform Framework Test Results

Check Status
Format
Init
Validate
Test Suite

Runs: 54 total, 54 passed, 0 failed, 0 skipped

Full test output
tests/normalization.tftest.hcl... in progress
  run "pattern_blocks_with_only_pattern_field_plans_clean"... pass
  run "merge_queue_with_partial_fields_plans_clean"... pass
  run "pull_request_with_only_merge_methods_plans_clean"... pass
  run "pages_partial_fields_plans_clean"... pass
  run "repo_with_environments_plans_clean"... pass
  run "archived_repo_plans_clean"... pass
  run "empty_repo_set_plans_clean"... pass
  run "org_mode_explicit_codeowners_plans_clean"... pass
  run "personal_mode_synthesizes_codeowners"... pass
  run "push_ruleset_on_private_when_supported_plans_clean"... pass
  run "license_template_defaults_null_not_MIT"... pass
  run "good_minimal_produces_expected_resource_counts"... pass
  run "good_minimal_carries_expected_defaults"... pass
  run "archived_repo_filters_out_downstream_locals"... pass
  run "empty_repo_set_exercises_every_filter_on_zero_input"... pass
  run "good_minimal_produces_zero_environments_zero_codeowners"... pass
  run "explicit_security_and_analysis_overrides_baseline"... pass
  run "multi_branch_sources_all_from_default_not_serially"... pass
  run "fork_repo_passes_through_source_fields"... pass
tests/normalization.tftest.hcl... tearing down
tests/normalization.tftest.hcl... pass
tests/preconditions.tftest.hcl... in progress
  run "rejects_invalid_visibility_enum"... pass
  run "rejects_public_repo_without_description"... pass
  run "rejects_invalid_ruleset_enforcement"... pass
  run "rejects_org_mode_codeowners_required_but_missing"... pass
  run "rejects_env_wait_timer_out_of_range"... pass
  run "rejects_env_branch_policy_mutually_exclusive"... pass
  run "rejects_actions_allowed_actions_enum"... pass
  run "rejects_actions_selected_without_config"... pass
tests/preconditions.tftest.hcl... tearing down
tests/preconditions.tftest.hcl... pass
tests/security.tftest.hcl... in progress
  run "strict_mode_no_gap_plans_clean"... pass
  run "compatibility_mode_no_gap_plans_clean_with_empty_preview"... pass
  run "strict_mode_reports_gaps_across_multiple_visibilities"... pass
  run "no_baseline_no_yaml_collapses_security_to_null"... pass
  run "baseline_feature_enabled_when_capability_matches"... pass
tests/security.tftest.hcl... tearing down
tests/security.tftest.hcl... pass
tests/validation.tftest.hcl... in progress
  run "good_minimal_plans_clean"... pass
  run "rejects_unknown_top_level_key"... pass
  run "rejects_unknown_nested_key"... pass
  run "rejects_duplicate_repo_keys"... pass
  run "rejects_unsupported_push_ruleset"... pass
  run "rejects_code_scanning_tool_typo"... pass
  run "rejects_multiple_nested_typos_in_one_repo"... pass
  run "rejects_secrets_written_as_map"... pass
  run "rejects_token_mode_missing_token"... pass
  run "rejects_app_mode_missing_app_auth"... pass
  run "rejects_token_mode_with_app_auth_also_set"... pass
  run "rejects_app_mode_with_token_also_set"... pass
  run "valid_app_auth_plans_clean"... pass
  run "strict_mode_fails_on_capability_gap"... pass
  run "compatibility_mode_tolerates_capability_gap"... pass
  run "push_ruleset_public_supports_true_still_fails"... pass
  run "push_ruleset_private_supports_false_fails"... pass
  run "push_ruleset_internal_supports_true_passes"... pass
  run "push_ruleset_internal_supports_false_fails"... pass
  run "rejects_invalid_github_owner_regex"... pass
  run "rejects_invalid_auth_mode_enum"... pass
  run "rejects_invalid_baseline_mode_enum"... pass
tests/validation.tftest.hcl... tearing down
tests/validation.tftest.hcl... pass

Success! 54 passed, 0 failed.

Commit: 1e2594a

@NWarila NWarila merged commit abda8e1 into main May 26, 2026
11 checks passed
@NWarila NWarila deleted the fix/conditional-allow-forking branch May 26, 2026 13:28
NWarila added a commit to NWarila/github-terraform-runner that referenced this pull request May 26, 2026
## Summary

Bumps the framework pin to the allow_forking ownership-aware default
fix.

| | Before | After |
|---|---|---|
| \`uses:\` SHA | \`0b99965b\` | \`abda8e1f\` |
| \`framework_ref:\` | \`0b99965b\` | \`abda8e1f\` |

## Why

After this merges, the next \`Deploy GitHub Terraform\` run on \`main\`
will succeed for personal-account private repos (Resume, Personal,
github-sandbox) because the framework now omits \`allow_forks\` from
PATCH payloads for that case. Was blocked by 422 since today's earlier
deploy attempt.

See
[nwarila-platform/github-terraform-framework#62](nwarila-platform/github-terraform-framework#62).

## Test plan

- [ ] PR validation green
- [ ] After merge: \`Deploy GitHub Terraform\` succeeds on main (no 422
errors on the 3 private repos)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NWarila added a commit that referenced this pull request May 26, 2026
## Summary

Bumps \`integrations/github\` from \`6.10.2\` to \`6.12.1\` (3 minor
versions). Motivated by the \`allow_forking\` provider-default behavior:
6.10.2 treats Terraform \`null\` as the schema default \`false\`, which
causes the provider to always include \`allow_forks\` in PATCH payloads.
PATCH then 422s on personal-account private repositories.

6.12.x may include nullable handling for \`allow_forking\` (the next
deploy will confirm). If the upgrade resolves the 422, no further code
changes are needed and the ownership-aware default landed in #62 will
behave as intended.

## Changes

- \`terraform/versions.tf\`: bump constraint \`6.10.2\` -> \`6.12.1\`.
- \`terraform/.terraform.lock.hcl\`: remove the github provider entry so
CI's \`terraform init\` regenerates it. AWS and time entries are
unchanged.

## Test plan

- [ ] Framework CI green (\`Validate & Test\` exercises terraform
init/validate/test).
- [ ] After merge: bump pin in
\`NWarila/github-terraform-runner/.github/workflows/terraform-deploy.yaml\`
and re-run \`Deploy GitHub Terraform\`. Expect the 3 personal-account
private repos (Resume, Personal, github-sandbox) to apply without 422.

## Fallback

If 6.12.1 doesn't fix the 422, follow-up PR adds \`managed:
optional(bool, true)\` to the YAML schema and filters at the locals
layer.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NWarila added a commit that referenced this pull request May 26, 2026
Adds a status preamble to REVIEW_REMEDIATION_PLAN.md recording
per-finding implementation state. 7 of 9 findings now marked Done
(Finding 4 closed by this session's PRs #62/#63); 1 REJECTED per
existing doc; 1 follow-up tracked (manage_codeowners_files opt-in flag).

## Test plan

- [ ] CI green (markdownlint)

Local: markdownlint-cli2 → 0 errors.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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