Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support repository level custom_property resource and custom_properties datasource #2316

Merged
merged 40 commits into from
Jan 17, 2025

Conversation

felixlut
Copy link
Contributor

@felixlut felixlut commented Jul 14, 2024

Contributes to #1956

Custom properties are defined on an organizational level, and then applied on a repository level. This PR aims to solve the latter of those two, while the former already has a PR for it in #2107.

Edit: Leaving the original comment below. Update can be found below in #2316 (review)

This is a work in progress (WIP) since the underlying sdk:s are lacking for the REST endpoints pertaining to the repository level custom properties:

If there's any progress on either of these I'll update this PR


Before the change?

  • It was not possible to add a custom property to a repo before this change (or read them)

After the change?

  • It is now possible to add a custom property to a repo before this change (or read them)

Pull request checklist

  • 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?

Please see our docs on breaking changes to help!

  • Yes
  • No

@ViktorLindgren95
Copy link

ViktorLindgren95 commented Sep 1, 2024

@felixlut It seems that v64 google/go-github#3240 added support for Multi select finally. Only issue is that the terraform provider codebase is still at v63

@felixlut
Copy link
Contributor Author

felixlut commented Sep 6, 2024

@felixlut It seems that v64 google/go-github#3240 added support for Multi select finally. Only issue is that the terraform provider codebase is still at v63

Seems like #2359 will deal with bumping the dep, so I'll revisit this once it's merged

@felixlut
Copy link
Contributor Author

felixlut commented Oct 9, 2024

I started looking into this again over the weekend but hit a bottleneck again. I made a fix in google/go-github#3309, but that requires another bump of the go-github provider. Hopefully that is the last blocker before we can use that provider fully for this feature.

The reason I'd rather use that than the go-sdk is that the PR simply becomes huge, and anytime another PR is merged I have to resolve conflicts (or at least run the risk of it). Dealing with that on a long-living PR like this is pretty painful. Using the go-github will skip that pain 😅

@stevehipwell
Copy link
Contributor

@felixlut given that go-github has been updated to v66 are you now unblocked?

@felixlut
Copy link
Contributor Author

@felixlut given that go-github has been updated to v66 are you now unblocked?

Will take a look again in the coming week or so

Copy link
Contributor Author

@felixlut felixlut left a comment

Choose a reason for hiding this comment

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

Aight, long time coming but I finally think this PR is in a reviewable state! I've made some comments on certain parts where I'm not 100% sure on the design, I'd love to get some opinions on those! Of the bat these 2 things are noteworthy as well:

  1. I opted for a non-authoriative custom_propertY resource as opposed to an authoriative custom_propertIES resource. Reason being that fulfills the use-case I have. I think an authoriative resource should exist, as it has its uses similarily to github_repository_collaborators. Nothing stops later PR:s doing just that
    • On a similar note I opted opted out of adding custom_property to github_repository, as proposed here for the same reason. Nothing stops future PR:s here either. Docs just needs to reflect that both shouldn't be used as the same time
  2. For the data_source I opted for custom_propertIES over reading each property individually. The reason for this is that the API endpoint for reading custom properties on a repo returns ALL properties. Filtering that full list and returning just 1 property seemed redundant to me. With that said creating a custom_propertY data source should be trivial with a future PR 😄

Also, thanks @ViktorLindgren95 for juggling some ideas regarding this PR with me, much appreciated!

Comment on lines +48 to +57
"property_value": {
Type: schema.TypeSet,
MinItems: 1,
Required: true,
Description: "Value of the custom property.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
ForceNew: true,
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The value of a custom property can be either null or string or array of strings because of the multi_select property type (docs). This is an interesting design choice in the API that poses some unintuitive challenges while interacting with it, namely how we design the Terraform resource for the property_value?

I opted for the "treat everything as an array of strings" solution, as that is how GitHub have solved the issue in their other API:s that deal with custom properties (eg the Organization Rulesets API, hidden deep in .conditions.repository_property_and_ref_name.repository_property.include.property_values). Like with the other comments, I'm all ears for opinions here

Comment on lines +35 to +41
"property_type": {
Type: schema.TypeString,
Required: true,
Description: "Type of the custom property",
ForceNew: true,
ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{SINGLE_SELECT, MULTI_SELECT, STRING, TRUE_FALSE}, false), "property_type"),
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

One of the downsides of the limitations mentioned in the comment below (property_value can be null or string or array) is that we need to know which format the API expects when creating/updating the custom property. I.e., an array with 1 element can either represent a multi_select property, and the API then expects that we send the full array, or it could be a single_select (or any other type actually), where the API would then expect only a string in the call to it. Docs for the specific endpoint can be found here.

I had the following ideas as workarounds:

  • User supplies the custom_property_type in the resource. More responsibility on the user, but it works...
  • Given the name of the custom property, read it on an org level and thereby derive the value_type. Downside of this is that the user/App running Terraform needs "Custom properties" organization permissions (read)
  • One would imagine that there should be a corresponding repo-level endpoint where one could read the value_type, but I've not been able to find one. This is the closest I could find, but that just returns the name and value of an already set property, which is not enough for our problem
  • It's also possible to run a "greedy" attempt at creating/updating the custom property when the array is of length 1. First try running it with the single string, and if that fails try again with the array instead. The error message is pretty helpful here (see below). This feels incredibly hacky though...

image

I opted for the user having to fill in custom_property_type but I'd love to be convinced there is another way here


func TestAccGithubRepositoryCustomPropertiesDataSource(t *testing.T) {

t.Skip("You need an org with custom properties already setup as described in the variables below") // TODO: at the time of writing org_custom_properties are not supported by this terraform provider, so cant be setup in the test itself for now
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've ran the tests locally and confirmed they work in my test org. Notably this should be updated once #2107 is merged (or another PR takes its place)


func TestAccGithubRepositoryCustomProperty(t *testing.T) {

t.Skip("You need an org with custom properties already setup as described in the variables below") // TODO: at the time of writing org_custom_properties are not supported by this terraform provider, so cant be setup in the test itself for now
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've ran the tests locally and confirmed they work in my test org. Notably this should be updated once #2107 is merged (or another PR takes its place)

Description: "Name of the repository which the custom properties should be on.",
},
"property": {
Type: schema.TypeSet,
Copy link
Contributor Author

@felixlut felixlut Nov 24, 2024

Choose a reason for hiding this comment

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

The way I see most people using this resource is to:

  1. Read the custom properties of a repo
  2. For each specific property do something based on it's value. Eg, do X to the repo if the owner property equals Y, do Z to the repo if the environment property equals ...

This means that it should be as easy as possible to get the value of a specific property. IMO that would be best achieved by using a TypeMap. Unfortunately that is not supported by v2 of the terraform-plugin-sdk (see thread).

The choice is then between TypeList or TypeSet. Reading the docs the latter sounds more correct to me, as the order of the custom properties does not matter. We've seen issues regarding this in this provider in the past, eg in #1950, that should be avoided by using TypeSet.

It's worth noting that there are some other implications of using TypeSet though, namely that they are non-indexable. This common pattern will for example not work, and throw the error below:

data "github_repository_custom_properties" "test" {
  repository    = "test-custom-properties"
}
locals {
  # Assuming the repo have a property called "string" set already
  test = data.github_repository_custom_properties.test.property[index(data.github_repository_custom_properties.test.property.*.property_name, "string")]
}

image

That does not mean that it's impossible to fetch the value of a custom property though. Either of the two snippets below does work, and are IMO more readable, but I think I'd be amiss if I didn't mention this.

data "github_repository_custom_properties" "test" {
  repository    = "test-custom-properties"
}
locals {
  # Get single custom property
  string_property = one([ for prop in data.github_repository_custom_properties.test.property: prop if prop.property_name == "string" ])
  # Convert the output of the data_source to a map that can then be referenced by `local.property_map[<property_name>]`
  property_map = { for prop in data.github_repository_custom_properties.test.property: prop.property_name => prop.property_value }
}

I personally lean towards using a TypeSet to avoid possible weird diffs, but I don't feel strongly about it. If anyone have any opinions here I'm all ears 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

@felixlut have you considered also adding a github_repository_custom_property data source to access a singular property? It would allow for more direct access to properties at the cost of increased API usage.

Copy link
Contributor Author

@felixlut felixlut Nov 26, 2024

Choose a reason for hiding this comment

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

I have, and nothing stops future PR:s for doing just that. It probably should exist, but you can't do everything in 1 PR 😅

The actual implementation would be to simply filter it after this call

@felixlut felixlut marked this pull request as ready for review November 24, 2024 10:29
@felixlut felixlut changed the title WIP: support repository_custom_properties resource and datasource feat: support repository_custom_properties resource and datasource Nov 24, 2024
@felixlut felixlut changed the title feat: support repository_custom_properties resource and datasource feat: support repository level custom_property resource and custom_properties datasource Nov 24, 2024
@ChaosCypher
Copy link

🚀

@hashildy
Copy link

Any update on getting this PR merged? This would be a really useful enhancement to support custom properties!

@felixlut
Copy link
Contributor Author

Aight, long time coming but I finally think this PR is in a reviewable state! I've made some comments on certain parts where I'm not 100% sure on the design, I'd love to get some opinions on those! Of the bat these 2 things are noteworthy as well:

  1. I opted for a non-authoriative custom_propertY resource as opposed to an authoriative custom_propertIES resource. Reason being that fulfills the use-case I have. I think an authoriative resource should exist, as it has its uses similarily to github_repository_collaborators. Nothing stops later PR:s doing just that
    • On a similar note I opted opted out of adding custom_property to github_repository, as proposed here for the same reason. Nothing stops future PR:s here either. Docs just needs to reflect that both shouldn't be used as the same time
  2. For the data_source I opted for custom_propertIES over reading each property individually. The reason for this is that the API endpoint for reading custom properties on a repo returns ALL properties. Filtering that full list and returning just 1 property seemed redundant to me. With that said creating a custom_propertY data source should be trivial with a future PR 😄

Also, thanks @ViktorLindgren95 for juggling some ideas regarding this PR with me, much appreciated!

@hashildy it hasn't moved since my last message ^^

It is ready for review though (or at least it was 2m ago).

@hashildy
Copy link

@stevehipwell - is this PR able to be reviewed/merged?

@stevehipwell
Copy link
Contributor

@hashildy I'm just a contributor, I have 9 PRs that have been ready to review merge in the last 3 months that haven't had a single comment by a maintainer.

@tayven-bigelow
Copy link

@kfcampbell - Any chance of getting a review / approval of this PR?
Custom properties would be great to have support around in terraform.

@kfcampbell kfcampbell merged commit 1ca7092 into integrations:main Jan 17, 2025
3 checks passed
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.

7 participants