Skip to content

fix: improve github_organization_custom_properties resource - #3234

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

fix: improve github_organization_custom_properties resource#3234
mkushakov wants to merge 17 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
Resolves #3580
Refs #2936


Before the change?

github_organization_custom_properties was the only way to manage org-level custom property definitions, and it had problems that could not be fixed in place without breaking existing state:

Per @stevehipwell's review feedback, this PR adds a correctly-named replacement rather than patching the old resource.

After the change?

New resource and data source github_organization_repository_custom_property (singular), written from scratch against ARCHITECTURE.md and the github_repository_custom_property refactor in #3476:

  • *Context CRUD returning diag.Diagnostics, tflog structured logging, and checkOrganizationOK on every entry point.
  • errors.AsType[*github.ErrorResponse] 404 handling: Read removes the property from state, Delete treats it as success.
  • ForceNew on the immutable property_name and value_type.
  • Plan-time validation, so misconfigurations fail before reaching the API: value_type and values_editable_by against their allowed sets, non-empty property_name and non-empty list elements, allowed_values required for single_select/multi_select and rejected for the other types, default_value limited to one element for everything but multi_select, and true_false defaults constrained to exactly "true"/"false".
  • default_value is a list of strings, which is what makes [BUG]: GitHub_organization_custom_properties #2806 and Proposal: Separate resource_github_organization_custom_properties into separate resources per value_type #3191 work. github.CustomProperty.DefaultValue is polymorphic — multi_select carries a JSON array, true_false a stringified bool, the rest plain strings — so requests send an array for multi_select and a bare string otherwise, and responses are normalised through the matching go-github accessor per value_type, raising an error rather than silently yielding "". Same "value is a collection, cardinality depends on the type" shape fix: Refactor repository custom property #3476 uses for property_value.
  • Timeouts block, and an import function that seeds property_name from the import ID.
  • Description on every schema attribute.

Deprecation: the existing github_organization_custom_properties resource and data source each gain a DeprecationMessage pointing at the new name. Their CRUD is otherwise untouched, so existing state and configurations keep working with only a warning.

Shared helpers: the polymorphic-value handling lives in a new github/util_custom_property.go. parseRepositoryCustomPropertyValueToStringSlice moves there too — it was already shared between github_repository_custom_property and its plural data source, so this is the only change to a pre-existing Go file beyond the deprecation notices and the provider registrations.

Tests: 18 t.Run subtests using ConfigStateChecks/ConfigPlanChecks, covering all five value_types (including scalar and multi-element defaults), create/update/import, both ForceNew paths, every validation rule, out-of-band deletion — both that a subsequent plan recreates rather than errors, and that destroying an already-deleted property succeeds — and the values_editable_by scenarios. Fixtures are created through the API with randomized names so the suite is safe to run in parallel against an org that already has properties.

Docs: no per-resource doc templates are needed — both pages render from the repo-level templates/{resources,data-sources}.md.tmpl fallbacks plus the schema Descriptions. Examples are in the auto-discovered layout including import.sh and import-by-string-id.tf; RESOURCES.md and ARCHITECTURE.md list the new resource/data source and flag the deprecated pair.

Note

On #3191: rather than splitting into one resource per value_type as originally proposed, the single resource now models every type correctly — including the array defaults that issue cites as its concrete example. Happy to revisit the split if you'd still prefer it.

Note

One known limitation: a default_value can be changed but not removed. CustomProperty.DefaultValue is an any field with omitempty, so a nil value is omitted from the request rather than sent as JSON null and GitHub keeps the previous default. The attribute is Computed, so the old value is absorbed back into state without erroring. This is documented in the attribute description.

Note

On #3580: the perpetual true_false drift is fixed in the new resource, which reads every default back through the accessor matching its value_type. The deprecated resource and data source keep their existing read path, so they still exhibit the drift until they are removed — anyone hitting that bug needs to move to the new name. Happy to patch the deprecated pair in place too, here or as a follow-up, if you'd rather not make migration a prerequisite for the fix.

Pull request checklist

  • Schema migrations have been created if needed (example) — N/A: new resource, no existing state to migrate; the 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 fully functional and only emit a deprecation warning.

@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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This comment targeted the in-place bug-fix approach on the old resource. Per @stevehipwell's suggestion below, that approach was replaced with a new resource (github_organization_repository_custom_property) instead, which uses *Context CRUD throughout. The old resource is now deprecated and left otherwise as-is so existing state keeps working — happy to revisit if you'd rather see it migrated too.

}
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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same as above — this was flagging Update calling Create then Read on the old resource. That code path is unchanged now that the fix lives in the new github_organization_repository_custom_property resource, whose Update doesn't call another 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 2 times, most recently 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)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done, with one deviation: I used the existing checkOrganizationOK helper (used by ~15 other data sources) instead of the inline fmt.Errorf check, since it returns diag.Diagnostics directly. Applied the same m any / meta, _ := m.(*Owner) / owner := meta.name shape to all 4 resource CRUD functions too, to match ARCHITECTURE.md and the #3476 pattern.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — replaced slices.Contains with a switch in both the data source and the resource's Read.

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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed by creating the fixture via mustCreateTestOrganizationRepositoryCustomProperty with a randomized name instead of a hardcoded one in the TF config. Also applied the same fix to the resource's own acceptance test (resource_github_organization_repository_custom_property_test.go), which had the identical problem across all 10 subtests plus no t.Parallel().


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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — switched to mustCreateTestOrganizationRepositoryCustomProperty(t, ...), matching the pattern in #3476.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — removed setOrganizationRepositoryCustomPropertyState and inlined the d.Set calls in Create/Read/Update and the data source's Read.

@abdonrd

abdonrd commented Aug 3, 2026

Copy link
Copy Markdown

@mkushakov are you still going ahead with this PR? Thanks! 🙏

@stevehipwell

Copy link
Copy Markdown
Collaborator

@mkushakov if you're unable to continue with this work, please let me know and I'll pick it up.

@stevehipwell stevehipwell added Type: Feature New feature or request Awaiting response and removed Type: Bug Something isn't working as documented labels Aug 10, 2026
@mkushakov
mkushakov force-pushed the fix/org-custom-properties-improvements branch from c19b5c4 to ff065db Compare August 11, 2026 16:24
mkushakov added a commit to mkushakov/terraform-provider-github that referenced this pull request Aug 11, 2026
… setting

Addresses review feedback from integrations#3234:
- Use m any + meta, _ := m.(*Owner) in every CRUD/read function, matching the
  convention documented in ARCHITECTURE.md and used by the integrations#3476 refactor of
  github_repository_custom_property. Keep checkOrganizationOK (used by ~15
  other data sources) rather than an inline org check.
- Replace slices.Contains with a switch statement for the select-type check.
- Drop the shared setOrganizationRepositoryCustomPropertyState helper and
  inline the d.Set calls in each CRUD method body, per stevehipwell's request
  to keep a single documented pattern until state-setting logic is unified
  repo-wide.
@github-actions github-actions Bot added the Type: Bug Something isn't working as documented label Aug 11, 2026
@mkushakov

Copy link
Copy Markdown
Author

@stevehipwell / @abdonrd , sorry for delay. I have time to work on it and try to address all comments. Feel free to let me know if there is some missing function or issues.

@deiga
deiga requested a balanced review from Copilot August 11, 2026 17:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

These provider review instructions are being used.

Adds singular organization repository custom-property resource/data source implementations while deprecating legacy plural APIs.

Changes:

  • Adds CRUD, validation, import, and acceptance tests.
  • Registers and documents the new APIs.
  • Adds deprecation guidance for legacy APIs.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
github/resource_github_organization_repository_custom_property.go New resource implementation.
github/resource_github_organization_repository_custom_property_test.go Resource acceptance tests.
github/data_source_github_organization_repository_custom_property.go New data source.
github/data_source_github_organization_repository_custom_property_test.go Data-source acceptance test.
github/provider.go Registers new APIs.
github/resource_github_organization_custom_properties.go Deprecates legacy resource.
github/data_source_github_organization_custom_properties.go Deprecates legacy data source.
templates/resources/organization_repository_custom_property.md.tmpl Resource documentation template.
templates/resources/organization_custom_properties.md.tmpl Resource deprecation notice.
templates/data-sources/organization_repository_custom_property.md.tmpl Data-source documentation template.
templates/data-sources/organization_custom_properties.md.tmpl Data-source deprecation notice.
examples/resources/github_organization_repository_custom_property/resource_1.tf Resource usage examples.
examples/resources/github_organization_repository_custom_property/import.sh CLI import example.
examples/resources/github_organization_repository_custom_property/import-by-string-id.tf Import-block example.
examples/data-sources/github_organization_repository_custom_property/data-source_1.tf Data-source example.
docs/resources/organization_repository_custom_property.md Generated resource documentation.
docs/resources/organization_custom_properties.md Generated resource deprecation notice.
docs/data-sources/organization_repository_custom_property.md Generated data-source documentation.
docs/data-sources/organization_custom_properties.md Generated data-source deprecation notice.
RESOURCES.md Updates implementation status.
ARCHITECTURE.md Records deprecated API replacements.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +59 to +63
"default_value": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Default value applied to repositories that do not explicitly set the property.",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch, and it was worse than described: DefaultValueString() returns ("", false) for both multi_select and true_false (it only handles string/single_select/url), and we discarded the ok bool. So true_false with a default_value didn't just lose data — it failed apply outright with "Provider produced inconsistent result after apply". Our own resource_1.tf example hit this.

Fixed in 729d750: default_value is now a TypeList of strings in both the resource and the data source, mirroring how #3476 models property_value. Requests send an array for multi_select and a bare string otherwise; responses go through a helper that picks the matching go-github accessor per value_type. Added true_false and multi_select acceptance subtests — both would have failed before this change.

Comment on lines +99 to +104
if selectType && len(allowedValues) == 0 {
return fmt.Errorf("allowed_values is required when value_type is %q", valueType)
}
if !selectType && len(allowedValues) > 0 {
return fmt.Errorf("allowed_values must not be set when value_type is %q", valueType)
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You're right that the description and the validation disagreed. I checked GitHub's REST docs though, and the requireddefault_value relationship isn't documented anywhere. Enforcing it at plan time would block configs that the API might well accept, which seems worse than the current behaviour of surfacing a 422 at apply. So I fixed the mismatch in the other direction — the description now says GitHub may reject required = true without a default, and there's no new CustomizeDiff rule. Happy to add the validation if maintainer think we should actually enforces it.

Comment thread github/resource_github_organization_repository_custom_property.go

@deiga deiga 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.

Partial review

Comment thread github/resource_github_organization_repository_custom_property.go
Comment thread github/resource_github_organization_repository_custom_property.go Outdated
Comment thread github/resource_github_organization_repository_custom_property.go Outdated
Comment thread github/resource_github_organization_repository_custom_property.go
Comment thread github/resource_github_organization_repository_custom_property.go Outdated
Comment thread github/resource_github_organization_repository_custom_property_test.go Outdated
Comment thread github/resource_github_organization_repository_custom_property_test.go Outdated
Comment thread templates/data-sources/organization_repository_custom_property.md.tmpl Outdated
Comment thread templates/resources/organization_repository_custom_property.md.tmpl Outdated
Comment thread github/resource_github_organization_repository_custom_property.go Outdated
@deiga
deiga requested a balanced review from Copilot August 12, 2026 18:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (8)

github/resource_github_organization_repository_custom_property.go:85

  • Empty allowed values pass the non-empty-list check, but expandStringList removes each empty string before the request. For example, [""] passes planning and becomes an empty API list. Validate each element as non-empty so invalid configurations fail at plan time.
				Elem:        &schema.Schema{Type: schema.TypeString},

github/resource_github_organization_repository_custom_property.go:121

  • The scalar cardinality check still accepts any string for true_false, even though this type can only encode "true" or "false". Invalid values therefore survive planning and fail at the GitHub API. Add type-specific diff validation (and a plan test) for this bounded value.
	if d.NewValueKnown("default_value") && valueType != github.PropertyValueTypeMultiSelect {
		if defaultValue, _ := d.Get("default_value").([]any); len(defaultValue) > 1 {
			return fmt.Errorf("default_value must contain at most one element when value_type is %q, got %d", valueType, len(defaultValue))
		}

github/resource_github_organization_repository_custom_property.go:344

  • This new 404-as-success delete branch is not exercised by the acceptance suite: the out-of-band deletion test recreates the property before final destroy. Add a test that removes the property immediately before Terraform destroy and verifies cleanup succeeds, so this stated behavior cannot regress.
		if ghErr, ok := errors.AsType[*github.ErrorResponse](err); ok && ghErr.Response.StatusCode == 404 {
			return nil
		}

github/resource_github_organization_repository_custom_property.go:86

  • allowed_values is both Optional and Computed, so when it is omitted on a new resource its planned value is unknown. The NewValueKnown("allowed_values") guard then skips the required-list check, allowing a single_select/multi_select plan to reach the API without values instead of producing the provider error asserted by the acceptance test. Make this field optional-only (the read path can still populate it) or inspect raw configuration explicitly.
				Computed:    true,

github/resource_github_organization_repository_custom_property.go:72

  • Empty elements pass schema validation, but expandStringList drops them. A configured default_value = [""] therefore plans successfully and is silently sent as no default, causing state to disagree with configuration. Reject empty elements during planning (or preserve them if the API supports them).

This issue also appears on line 85 of the same file.

				Elem:        &schema.Schema{Type: schema.TypeString},

github/resource_github_organization_repository_custom_property.go:22

  • url is exposed as a supported value type, but the new acceptance suite never creates or imports a URL definition/default despite the PR stating that all five types are covered. Add a URL subtest that verifies its scalar default round-trips through the API and import path.
	string(github.PropertyValueTypeURL),

github/resource_github_organization_repository_custom_property.go:144

  • Clearing a previously configured default cannot work here. For default_value = [], GetOk is false, leaving the any field nil; go-github omits that field from the PATCH payload, so GitHub retains the old default and the response restores it into state. Encode an explicit JSON null for an empty configured list and add an update test that changes a non-empty default to empty.
	if v, ok := d.GetOk("default_value"); ok {
		if defaultValue := expandStringList(v.([]any)); len(defaultValue) > 0 {
			// Only multi_select sends an array; the other types send a bare string.
			switch valueType {
			case github.PropertyValueTypeMultiSelect:

github/resource_github_organization_repository_custom_property.go:54

  • A required string still accepts property_name = "", which produces an invalid API path and defers a deterministic input error until apply. Add plan-time non-empty validation to this new schema attribute.

This issue also appears on line 118 of the same file.

				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				Description: "Name of the custom property.",
			},

…erties resource

Adding the DeprecationMessage field left the remaining Create/Read/Update/Delete
fields misaligned.
… setting

Addresses review feedback from integrations#3234:
- Use m any + meta, _ := m.(*Owner) in every CRUD/read function, matching the
  convention documented in ARCHITECTURE.md and used by the integrations#3476 refactor of
  github_repository_custom_property. Keep checkOrganizationOK (used by ~15
  other data sources) rather than an inline org check.
- Replace slices.Contains with a switch statement for the select-type check.
- Drop the shared setOrganizationRepositoryCustomPropertyState helper and
  inline the d.Set calls in each CRUD method body, per stevehipwell's request
  to keep a single documented pattern until state-setting logic is unified
  repo-wide.
Addresses stevehipwell's feedback that the data-source test needs to work in
an organization that already has custom properties, and applies the same fix
to the resource acceptance test, which had the identical problem: every
subtest used a hardcoded tf-acc-test-* property name with no t.Parallel(),
so concurrent runs against one org (or a leftover from a failed run) would
collide.

Also switches the data-source test to create its fixture via
mustCreateTestOrganizationRepositoryCustomProperty instead of a
resource block in the TF config, per the pattern in integrations#3476.
…y pattern

Copies the pattern from integrations#3476 (github_repository_custom_property):

- Rewrite both templates to be schema-driven ({{ .Description }}, subcategory,
  HasExamples/ExampleFiles, HasImport/HasImportIDConfig/HasImportIdentityConfig)
  instead of hardcoded front-matter prose, so the rendered docs can't drift
  from the resource/data-source Description fields again.
- Move examples to the auto-discovered examples/{resources,data-sources}/
  github_organization_repository_custom_property/ layout. Fold the three
  previously separately-headed usage examples (default, editable-by,
  boolean) into a single resource_1.tf with comment headers, per the
  single-example-file convention in examples.instructions.md.
- Add import-by-string-id.tf for the modern `import` block syntax.
- Regenerate docs/resources and docs/data-sources for this resource.
…iner indexes

RESOURCES.md and ARCHITECTURE.md list every resource/data source and its
deprecation status; both were missing the new
github_organization_repository_custom_property resource and data source,
and the (🚫) deprecation marker on github_organization_custom_properties.
github.CustomProperty.DefaultValue is polymorphic: multi_select carries an
array, true_false a stringified bool, and the rest plain strings. The code
called only DefaultValueString() and discarded its ok bool, which returns
("", false) for both multi_select AND true_false, so:

- true_false properties with a default_value failed apply outright with
  "Provider produced inconsistent result after apply" — the committed
  resource_1.tf example hit this.
- multi_select list defaults could not be represented at all (integrations#2806).

default_value is now a TypeList of strings in both the resource and the data
source, mirroring how integrations#3476 models property_value on
github_repository_custom_property. Requests send an array for multi_select and
a bare string otherwise; responses are normalised by
flattenOrganizationRepositoryCustomPropertyDefaultValue, which selects the
matching go-github accessor per value_type and returns an error rather than
silently yielding "" when a value cannot be parsed. CustomizeDiff rejects
multi-element default_value for the four scalar types.

Also addresses review feedback:
- Timeouts block so users can configure per-operation timeouts.
- Real import function seeding property_name from the ID, letting Read use the
  attribute getter instead of d.Id().
- diag.Errorf instead of diag.FromErr(fmt.Errorf(...)), the dominant idiom in
  this package.
- Guard against the API returning an empty property name.
- Soften the `required` description: GitHub's REST docs do not actually
  document that default_value is mandatory when required is true, so this is
  no longer stated as a hard rule.
…e plans

New subtests, closing gaps that let the default_value bug ship green:
- true_false with a default_value, which previously failed apply with
  "Provider produced inconsistent result after apply".
- multi_select with multiple default values, the integrations#2806 scenario.
- a scalar type rejecting a multi-element default_value.
- out-of-band deletion via the API, asserting the next plan is a Create so the
  graceful-404 handling in Read cannot regress.
- default_value is an empty list (not [""]) when the property has no default.

Addresses deiga's review feedback:
- Collapse every before/after config pair into one parameterised template
  string instead of duplicated configBefore/configAfter blocks.
- Assert ResourceActionUpdate on the in-place update steps.
Updates the resource example to the new default_value list syntax, adds a
multi_select example demonstrating multiple default values (integrations#2806), and
regenerates both pages so the schema tables and the new timeouts block match
the code.
flattenOrganizationRepositoryCustomPropertyDefaultValue is used by both the
resource and the data source, so per review feedback it moves out of the
resource file into github/util_custom_property.go, following the
util_<domain>.go convention in ARCHITECTURE.md.

buildOrganizationRepositoryCustomProperty and the two value-type var blocks
stay put — they are only used by the resource.
Per review feedback the narrative note doesn't belong in the doc template. The
pointer to github_repository_custom_property and the REST API link move into a
top-of-file comment in resource_1.tf, which examples.instructions.md names as
the place for extra context. The template now holds nothing beyond the
repo-standard front matter.
…l file

parseRepositoryCustomPropertyValueToStringSlice is shared by
data_source_github_repository_custom_properties.go and
resource_github_repository_custom_property.go, so it joins the other custom
property helper in util_custom_property.go rather than living in a data source
file. Requested in review.
Four schema/diff correctness fixes from review:

- allowed_values is no longer Computed. An omitted Optional+Computed list is
  unknown during plan (schemaMap.diffList marks the count NewComputed when both
  old and new lengths are 0), so d.NewValueKnown returned false and the
  cross-field validation skipped itself -- meaning "allowed_values is required
  when value_type is single_select" could never fire and the request reached the
  API without values. Nothing needed the Computed behaviour: select types always
  set the field in config, and Read clears it for the other types.
- Reject empty strings in allowed_values and default_value elements.
  expandStringList silently drops "", so allowed_values = [""] passed the
  length check and became an empty API list, and default_value = [""] became no
  default at all while the config said otherwise.
- Constrain true_false defaults to exactly "true"/"false". strconv.ParseBool
  accepts "True" and "1", which the read path would normalise back to "true" and
  fail the apply with an inconsistent-result error.
- Reject an empty property_name on both the resource and the data source rather
  than building an invalid API path.

Also documents that a default_value cannot be removed once set: the API field is
omitted when nil rather than sent as JSON null, so GitHub keeps the old value.
…dations

- url subtest with a scalar default and import, so all five value_types are
  genuinely exercised as the PR claims.
- Destroy step whose PreConfig removes the property out of band, covering the
  404-as-success branch in Delete. The existing recreate test always recreates
  the property first, so that branch was never reached.
- Plan-only subtests for the two new rules: a non-boolean true_false default
  ("True", which strconv.ParseBool would otherwise accept and silently
  normalise) and an empty string in allowed_values.
Both templates were byte-for-byte identical to the repo-level fallbacks at
templates/resources.md.tmpl and templates/data-sources.md.tmpl, which
tfplugindocs uses when no per-resource template exists. Verified the generated
pages are unchanged with the templates removed.

Also picks up the default_value description note from the previous commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Awaiting response Type: Bug Something isn't working as documented Type: Feature New feature or request

Projects

None yet

5 participants