Skip to content

fix: improve github_organization_custom_properties resource#3234

Open
mkushakov wants to merge 5 commits into
integrations:mainfrom
mkushakov:fix/org-custom-properties-improvements
Open

fix: improve github_organization_custom_properties resource#3234
mkushakov wants to merge 5 commits into
integrations:mainfrom
mkushakov:fix/org-custom-properties-improvements

Conversation

@mkushakov

@mkushakov mkushakov commented Feb 27, 2026

Copy link
Copy Markdown

Resolves #2806
Resolves #3191
Refs #2936


Before the change?

  • The only way to manage org-level custom property definitions was github_organization_custom_properties (plural).
  • That resource had several correctness issues: a CustomizeDiff that referenced a non-existent slug field, a redundant double-read in Update, no 404 handling in Read (drift outside Terraform errored instead of removing from state), and Get/GetOk confusion on optional fields that masked unset vs. empty.
  • value_type was optional on the resource even though the API requires it, and property_name/value_type were not ForceNew even though they are immutable server-side, so plans silently corrupted state.
  • No validation existed for allowed_values against value_type, so misconfigurations only surfaced on the API call.
  • The plural naming did not match the resource's behaviour: it manages a single property at a time (see [MAINT]: Naming of organization custom properties needs the change (from plural to singular) #2936).

After the change?

Following @stevehipwell's review feedback, the in-place bug-fix approach has been replaced with a new, correctly-named resource and data source, leaving the old ones in place but marked deprecated:

  • New resource github_organization_repository_custom_property (singular) implemented from scratch using the conventions in ARCHITECTURE.md and the pattern established by the github_repository_custom_property refactor in fix: Refactor repository custom property #3476:
  • *Context CRUD with diag.Diagnostics
  • tflog structured logging
  • errors.AsType[*github.ErrorResponse] for graceful 404 handling on Read/Delete
  • customdiff.All cross-field validation for allowed_values (required for single_select/multi_select, rejected for the other types)
  • ForceNew on the immutable property_name and value_type
  • checkOrganization guard on every CRUD entry point
  • Description on every schema attribute
  • New data source github_organization_repository_custom_property mirroring the resource schema.
  • Old github_organization_custom_properties resource and data source carry a DeprecationMessage pointing at the new singular names. CRUD is left intact so existing state continues to work.
  • Acceptance tests use the modern ConfigStateChecks / ConfigPlanChecks (statecheck / plancheck) pattern with t.Run subtests covering create, update, import, ForceNew, cross-field validation, and the values_editable_by scenarios.
  • Templates, examples, and import.sh added; make generatedocs ran.

Pull request checklist

  • Schema migrations have been created if needed (example) — N/A: new resource, no existing state to migrate; deprecated resource keeps its current schema.
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been reviewed and added / updated if needed (for bug fixes / features)

Does this introduce a breaking change?

  • Yes
  • No

The old resource and data source remain functional and only emit a deprecation warning, so existing configurations continue to work without modification.

@github-actions

Copy link
Copy Markdown

👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀

Comment on lines 17 to 22
Create: resourceGithubCustomPropertiesCreate,
Read: resourceGithubCustomPropertiesRead,
Update: resourceGithubCustomPropertiesUpdate,
Delete: resourceGithubCustomPropertiesDelete,
Importer: &schema.ResourceImporter{
State: resourceGithubCustomPropertiesImport,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor to use the Context functions

}
return resourceGithubCustomPropertiesRead(d, meta)
// Create uses the same upsert API, and already calls Read at the end
return resourceGithubCustomPropertiesCreate(d, meta)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Never call any CRUD function

@deiga deiga added this to the v6 Next milestone Apr 16, 2026
@mkushakov mkushakov force-pushed the fix/org-custom-properties-improvements branch from c8c34c4 to b4fdf7b Compare April 17, 2026 06:56
@deiga deiga removed this from the v6 Next milestone Apr 19, 2026
@mkushakov mkushakov force-pushed the fix/org-custom-properties-improvements branch 2 times, most recently from 1267efc to e40236c Compare April 27, 2026 14:33
@stevehipwell

Copy link
Copy Markdown
Collaborator

@mkushakov if you're still interested in contributing this change, I think it may be better to create a new resource named correctly (#2936) and then deprecate this resource. Off the top of my head I think the resource should be github_organization_repository_custom_property so as to not collide with the organization level custom properties (which should probably be github_enterprise_organization_custom_property & github_organization_custom_property respectively).

I've done a full refactor on the github_repository_custom_property resource in #3476, which you could use as a basis. Please note that we've updated the contributing guidelines and added an architecture doc to help contributors.

@mkushakov mkushakov force-pushed the fix/org-custom-properties-improvements branch from e40236c to 8122d85 Compare June 12, 2026 10:38
- Remove broken CustomizeDiff referencing non-existent 'slug' field
- Make 'property_name' ForceNew (renaming creates a new property, orphans old)
- Make 'value_type' Required and ForceNew (can't create without type, changing type requires recreation)
- Add allowed_values validation: required for select types, rejected for others
- Add checkOrganization guard to all CRUD functions
- Add graceful 404 handling in Read (removes from state instead of erroring)
- Improve error messages with context
- Fix Update double-read (Create already calls Read at the end)
- Add resource Description
- Use GetOk for optional fields to avoid sending zero values

Resolves integrations#2806
@mkushakov mkushakov force-pushed the fix/org-custom-properties-improvements branch from 8122d85 to c19b5c4 Compare June 14, 2026 15:58
@mkushakov

Copy link
Copy Markdown
Author

@mkushakov if you're still interested in contributing this change, I think it may be better to create a new resource named correctly (#2936) and then deprecate this resource. Off the top of my head I think the resource should be github_organization_repository_custom_property so as to not collide with the organization level custom properties (which should probably be github_enterprise_organization_custom_property & github_organization_custom_property respectively).

I've done a full refactor on the github_repository_custom_property resource in #3476, which you could use as a basis. Please note that we've updated the contributing guidelines and added an architecture doc to help contributors.

hello @stevehipwell thanks for suggestions, as you asked i have following in my PR:

  • created a new resource and data source github_organization_custom_property (singular)
  • marked existing github_organization_custom_properties as deprecated
    Please let me know if you see any issues with my PR and I am open for suggestions. I have also updated PR body to reflect those changes

@stevehipwell stevehipwell left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes @mkushakov, this looks like a great addition. As well as the inline comments I've got the following high level feedback.

  • We need a github_organization_repository_custom_properties data source to replace github_organization_custom_properties
  • Could you align your changes/patterns to #3476 (this should be "correct" in terms of the contribution guide)

Comment on lines +61 to +68
func dataSourceGithubOrganizationRepositoryCustomPropertyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
if err := checkOrganization(meta); err != nil {
return diag.FromErr(err)
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
propertyName := d.Get("property_name").(string)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func dataSourceGithubOrganizationRepositoryCustomPropertyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
if err := checkOrganization(meta); err != nil {
return diag.FromErr(err)
}
client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
propertyName := d.Get("property_name").(string)
func dataSourceGithubOrganizationRepositoryCustomPropertyRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
meta, _ := m.(*Owner)
client := meta.v3client
owner := meta.name
if !meta.IsOrganization {
return return diag.FromErr(fmt.Errorf("repository custom properties are only supported for organizations, %q is a user", owner))
}
propertyName := d.Get("property_name").(string)

Comment on lines +80 to +85
if !slices.Contains([]github.PropertyValueType{
github.PropertyValueTypeSingleSelect,
github.PropertyValueTypeMultiSelect,
}, cp.ValueType) {
cp.AllowedValues = nil
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a switch statement would work better here.

func TestAccGithubOrganizationRepositoryCustomPropertyDataSource(t *testing.T) {
const dataAddr = "data.github_organization_repository_custom_property.test"

t.Run("reads a property created by the resource", func(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this test work correctly in an organization where there are already existing properties?


t.Run("reads a property created by the resource", func(t *testing.T) {
config := `
resource "github_organization_repository_custom_property" "test" {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you look at #3476 and copy the pattern for configuring the test dependencies outside of TF code?

cp.AllowedValues = nil
}

if err := setOrganizationRepositoryCustomPropertyState(d, cp); err != nil {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for now I'd rather see the code duplicated in the method bodies, as if we move the logic we ought to have a single documented pattern for how to do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug Something isn't working as documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: Separate resource_github_organization_custom_properties into separate resources per value_type [BUG]: GitHub_organization_custom_properties

3 participants