Skip to content

Support non-Enterprise GitHub organizations #6

@tstollin

Description

@tstollin

Support non-Enterprise GitHub organizations

Summary

git-hubby currently requires GitHub Enterprise Cloud for full organization management. Several reconciled features depend on Enterprise-only APIs and will fail with 403 or 404 errors when applied to organizations on Free, Pro, or Team plans. This issue tracks the work needed to make the operator compatible with all GitHub plan tiers.

Background

The following features are Enterprise Cloud-only and currently always reconciled:

Feature API Endpoint Plan Requirement
Organization rulesets PUT /orgs/{org}/rulesets Enterprise Cloud
Code security configurations POST /orgs/{org}/code-security/configurations Enterprise Cloud (GHAS)
IDP group sync for teams PATCH /teams/{team_id}/team-sync/group-mappings Enterprise Cloud

All other features (org settings, repositories, repo rulesets, webhooks, teams, custom properties, actions settings) work on all plans.

Proposed Approach

Add a plan field to OrganizationSpec that declares the GitHub plan tier. Reconcilers conditionally skip Enterprise-only tasks based on this field. This follows the operator's philosophy of explicit, declarative configuration.

Alternative considered

Graceful degradation (detect 403/404 and skip) was considered but rejected because:

  • 403 is ambiguous — could be a permissions issue vs. a plan limitation
  • Wastes API calls attempting unsupported operations
  • Fails silently, making debugging harder

Required Changes

1. CRD: Add plan field to OrganizationSpec

File: api/v1alpha1/organization_types.go

Add a Plan field to OrganizationSpec:

// Plan indicates the GitHub plan tier for this organization.
// Determines which Enterprise-only features are reconciled.
// +kubebuilder:validation:Enum=enterprise;team;free
// +kubebuilder:default=enterprise
// +optional
Plan string `json:"plan,omitempty"`

Default is enterprise to maintain backward compatibility.

2. Organization reconciler: Conditional reconciliation groups

File: internal/reconciler/orgrec/reconciler.go

Make RequiredReconciliations() conditional based on plan:

  • All plans: org settings, custom properties, actions settings
  • Enterprise only: organization rulesets, code security configurations

The RequiredReconciliations() method should build the group dynamically instead of returning a static slice.

3. Team reconciler: Conditional IDP sync

File: internal/reconciler/teamrec/reconciler.go

Skip IDP group sync reconciliation when the team's organizations are not on the Enterprise plan. Since teams can span multiple organizations, this requires checking the plan of each referenced organization.

4. Conditions: Dynamic required conditions

File: internal/conditions/conditions.go

The SetReadyCondition() aggregation currently hardcodes all condition types as required. When Enterprise-only features are skipped, their conditions won't be set, so the aggregation must only consider conditions for enabled features.

Options:

  • Accept the list of required condition types as a parameter
  • Have reconcilers declare which conditions are relevant (already implicit in RequiredReconciliations())

5. Validation webhook: Reject invalid plan/feature combinations

File: internal/webhook/v1alpha1/organization_webhook.go

Add validation that rejects Enterprise-only spec fields when plan != "enterprise":

  • Reject non-empty spec.rulesets on free or team plans
  • Reject non-empty spec.codeSecurityConfigurations on free or team plans

This gives users early feedback rather than runtime failures.

6. Status: Surface skipped features

Consider adding a condition or event indicating which features were skipped due to plan tier, so users understand why certain features are not being reconciled.

7. Regenerate and test

  • Run make manifests generate to regenerate CRDs and deepcopy methods
  • Update Helm chart CRDs
  • Add unit tests for conditional reconciliation groups
  • Add unit tests for webhook validation of plan/feature combinations
  • Add tests for the Ready condition aggregation with subsets of conditions
  • Update documentation (README, CONTRIBUTING.md, copilot-instructions)

Acceptance Criteria

  • Organization CRD has a plan field with values enterprise, team, free (default: enterprise)
  • Organization reconciler skips rulesets and code security configurations for non-enterprise plans
  • Team reconciler skips IDP group sync for non-enterprise organizations
  • Validation webhook rejects Enterprise-only fields on non-enterprise plans
  • Ready condition correctly aggregates only the conditions for enabled features
  • Existing behavior is unchanged when plan is unset or enterprise (backward compatible)
  • Unit tests cover all plan tiers
  • Documentation updated

Notes

  • Repository rulesets work on Team+ plans (and Free for public repos). A future enhancement could add more granular plan-based feature gating at the repository level, but this is out of scope for this issue.
  • The plan field is purely informational to the operator — it does not query GitHub to verify the actual plan. Users are responsible for setting it correctly.

This issue was drafted with the help of GitHub Copilot.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions