diff --git a/github/data_source_github_repository_custom_properties.go b/github/data_source_github_repository_custom_properties.go new file mode 100644 index 0000000000..d4622861ff --- /dev/null +++ b/github/data_source_github_repository_custom_properties.go @@ -0,0 +1,103 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubRepositoryCustomProperties() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGithubOrgaRepositoryCustomProperties, + + Schema: map[string]*schema.Schema{ + "repository": { + Type: schema.TypeString, + Required: true, + Description: "Name of the repository which the custom properties should be on.", + }, + "property": { + Type: schema.TypeSet, + Computed: true, + Description: "List of custom properties", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "property_name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the custom property.", + }, + "property_value": { + Type: schema.TypeSet, + Computed: true, + Description: "Value of the custom property.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceGithubOrgaRepositoryCustomProperties(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + ctx := context.Background() + + owner := meta.(*Owner).name + + repoName := d.Get("repository").(string) + + allCustomProperties, _, err := client.Repositories.GetAllCustomPropertyValues(ctx, owner, repoName) + if err != nil { + return err + } + + results, err := flattenRepositoryCustomProperties(allCustomProperties) + if err != nil { + return err + } + + d.SetId(buildTwoPartID(owner, repoName)) + d.Set("repository", repoName) + d.Set("property", results) + + return nil +} + +func flattenRepositoryCustomProperties(customProperties []*github.CustomPropertyValue) ([]interface{}, error) { + + results := make([]interface{}, 0) + for _, prop := range customProperties { + result := make(map[string]interface{}) + + result["property_name"] = prop.PropertyName + + propertyValue, err := parseRepositoryCustomPropertyValueToStringSlice(prop) + if err != nil { + return nil, err + } + + result["property_value"] = propertyValue + + results = append(results, result) + } + + return results, nil +} + +func parseRepositoryCustomPropertyValueToStringSlice(prop *github.CustomPropertyValue) ([]string, error) { + switch value := prop.Value.(type) { + case string: + return []string{value}, nil + case []string: + return value, nil + default: + return nil, fmt.Errorf("custom property value couldn't be parsed as a string or a list of strings: %s", value) + } +} diff --git a/github/data_source_github_repository_custom_properties_test.go b/github/data_source_github_repository_custom_properties_test.go new file mode 100644 index 0000000000..714f35457f --- /dev/null +++ b/github/data_source_github_repository_custom_properties_test.go @@ -0,0 +1,233 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +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 + singleSelectPropertyName := "single-select" // Needs to be a of type single_select, and have "option1" as an option + multiSelectPropertyName := "multi-select" // Needs to be a of type multi_select, and have "option1" and "option2" as an options + trueFlasePropertyName := "true-false" // Needs to be a of type true_false, and have "option1" as an option + stringPropertyName := "string" // Needs to be a of type string, and have "option1" as an option + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + t.Run("creates custom property of type single_select without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "single_select" + property_value = ["option1"] + } + data "github_repository_custom_properties" "test" { + repository = github_repository_custom_property.test.repository + } + `, randomID, singleSelectPropertyName) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemNestedAttrs("data.github_repository_custom_properties.test", + "property.*", map[string]string{ + "property_name": singleSelectPropertyName, + "property_value.#": "1", + "property_value.0": "option1", + }), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("creates custom property of type multi_select without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "multi_select" + property_value = ["option1", "option2"] + } + data "github_repository_custom_properties" "test" { + repository = github_repository_custom_property.test.repository + } + `, randomID, multiSelectPropertyName) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemNestedAttrs("data.github_repository_custom_properties.test", + "property.*", map[string]string{ + "property_name": multiSelectPropertyName, + "property_value.#": "2", + "property_value.0": "option1", + "property_value.1": "option2", + }), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("creates custom property of type true_false without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "true_false" + property_value = ["true"] + } + data "github_repository_custom_properties" "test" { + repository = github_repository_custom_property.test.repository + } + `, randomID, trueFlasePropertyName) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemNestedAttrs("data.github_repository_custom_properties.test", + "property.*", map[string]string{ + "property_name": trueFlasePropertyName, + "property_value.#": "1", + "property_value.0": "true", + }), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("creates custom property of type string without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "string" + property_value = ["text"] + } + data "github_repository_custom_properties" "test" { + repository = github_repository_custom_property.test.repository + } + `, randomID, stringPropertyName) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemNestedAttrs("data.github_repository_custom_properties.test", + "property.*", map[string]string{ + "property_name": stringPropertyName, + "property_value.#": "1", + "property_value.0": "text", + }), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) +} diff --git a/github/provider.go b/github/provider.go index a9a04d0474..8f44c95098 100644 --- a/github/provider.go +++ b/github/provider.go @@ -172,6 +172,7 @@ func Provider() *schema.Provider { "github_repository_dependabot_security_updates": resourceGithubRepositoryDependabotSecurityUpdates(), "github_repository_collaborator": resourceGithubRepositoryCollaborator(), "github_repository_collaborators": resourceGithubRepositoryCollaborators(), + "github_repository_custom_property": resourceGithubRepositoryCustomProperty(), "github_repository_deploy_key": resourceGithubRepositoryDeployKey(), "github_repository_deployment_branch_policy": resourceGithubRepositoryDeploymentBranchPolicy(), "github_repository_environment": resourceGithubRepositoryEnvironment(), @@ -241,6 +242,7 @@ func Provider() *schema.Provider { "github_repository": dataSourceGithubRepository(), "github_repository_autolink_references": dataSourceGithubRepositoryAutolinkReferences(), "github_repository_branches": dataSourceGithubRepositoryBranches(), + "github_repository_custom_properties": dataSourceGithubRepositoryCustomProperties(), "github_repository_environments": dataSourceGithubRepositoryEnvironments(), "github_repository_deploy_keys": dataSourceGithubRepositoryDeployKeys(), "github_repository_deployment_branch_policies": dataSourceGithubRepositoryDeploymentBranchPolicies(), diff --git a/github/resource_github_repository_custom_property.go b/github/resource_github_repository_custom_property.go new file mode 100644 index 0000000000..6de2a7dcee --- /dev/null +++ b/github/resource_github_repository_custom_property.go @@ -0,0 +1,166 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +const ( + SINGLE_SELECT = "single_select" + MULTI_SELECT = "multi_select" + STRING = "string" + TRUE_FALSE = "true_false" +) + +func resourceGithubRepositoryCustomProperty() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubRepositoryCustomPropertyCreate, + Read: resourceGithubRepositoryCustomPropertyRead, + Delete: resourceGithubRepositoryCustomPropertyDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "repository": { + Type: schema.TypeString, + Required: true, + Description: "Name of the repository which the custom properties should be on.", + ForceNew: true, + }, + "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"), + }, + "property_name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the custom property.", + ForceNew: true, + }, + "property_value": { + Type: schema.TypeSet, + MinItems: 1, + Required: true, + Description: "Value of the custom property.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + ForceNew: true, + }, + }, + } +} + +func resourceGithubRepositoryCustomPropertyCreate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + ctx := context.Background() + + owner := meta.(*Owner).name + repoName := d.Get("repository").(string) + propertyName := d.Get("property_name").(string) + propertyType := d.Get("property_type").(string) + propertyValue := expandStringList(d.Get("property_value").(*schema.Set).List()) + + customProperty := github.CustomPropertyValue{ + PropertyName: propertyName, + } + + // The propertyValue can either be a list of strings or a string + switch propertyType { + case SINGLE_SELECT, TRUE_FALSE, STRING: + customProperty.Value = propertyValue[0] + case MULTI_SELECT: + customProperty.Value = propertyValue + default: + return fmt.Errorf("custom property type is not valid: %v", propertyType) + } + + _, err := client.Repositories.CreateOrUpdateCustomProperties(ctx, owner, repoName, []*github.CustomPropertyValue{&customProperty}) + if err != nil { + return err + } + + d.SetId(buildThreePartID(owner, repoName, propertyName)) + + return resourceGithubRepositoryCustomPropertyRead(d, meta) +} + +func resourceGithubRepositoryCustomPropertyRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + ctx := context.Background() + + owner, repoName, propertyName, err := parseThreePartID(d.Id(), "owner", "repoName", "propertyName") + if err != nil { + return err + } + + wantedCustomPropertyValue, err := readRepositoryCustomPropertyValue(ctx, client, owner, repoName, propertyName) + if err != nil { + return err + } + + d.SetId(buildThreePartID(owner, repoName, propertyName)) + d.Set("repository", repoName) + d.Set("property_name", propertyName) + d.Set("property_value", wantedCustomPropertyValue) + + return nil +} + +func resourceGithubRepositoryCustomPropertyDelete(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*Owner).v3client + ctx := context.Background() + + owner, repoName, propertyName, err := parseThreePartID(d.Id(), "owner", "repoName", "propertyName") + if err != nil { + return err + } + + customProperty := github.CustomPropertyValue{ + PropertyName: propertyName, + Value: nil, + } + + _, err = client.Repositories.CreateOrUpdateCustomProperties(ctx, owner, repoName, []*github.CustomPropertyValue{&customProperty}) + if err != nil { + return err + } + + return nil +} + +func readRepositoryCustomPropertyValue(ctx context.Context, client *github.Client, owner, repoName, propertyName string) ([]string, error) { + allCustomProperties, _, err := client.Repositories.GetAllCustomPropertyValues(ctx, owner, repoName) + if err != nil { + return nil, err + } + + var wantedCustomProperty *github.CustomPropertyValue + for _, customProperty := range allCustomProperties { + if customProperty.PropertyName == propertyName { + wantedCustomProperty = customProperty + } + } + + if wantedCustomProperty == nil { + return nil, fmt.Errorf("could not find a custom property with name: %s", propertyName) + } + + wantedPropertyValue, err := parseRepositoryCustomPropertyValueToStringSlice(wantedCustomProperty) + if err != nil { + return nil, err + } + + return wantedPropertyValue, nil +} diff --git a/github/resource_github_repository_custom_property_test.go b/github/resource_github_repository_custom_property_test.go new file mode 100644 index 0000000000..eba1590117 --- /dev/null +++ b/github/resource_github_repository_custom_property_test.go @@ -0,0 +1,209 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +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 + singleSelectPropertyName := "single-select" // Needs to be a of type single_select, and have "option1" as an option + multiSelectPropertyName := "multi-select" // Needs to be a of type multi_select, and have "option1" and "option2" as an options + trueFlasePropertyName := "true-false" // Needs to be a of type true_false + stringPropertyName := "string" // Needs to be a of type string + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + t.Run("creates custom property of type single_select without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "single_select" + property_value = ["option1"] + } + `, randomID, singleSelectPropertyName) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_name", singleSelectPropertyName), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.#", "1"), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.0", "option1"), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: check, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("creates custom property of type multi_select without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "multi_select" + property_value = ["option1", "option2"] + } + `, randomID, multiSelectPropertyName) + + checkWithOwner := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_name", multiSelectPropertyName), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.#", "2"), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.0", "option1"), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.1", "option2"), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: checkWithOwner, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("creates custom property of type true-false without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "true_false" + property_value = ["true"] + } + `, randomID, trueFlasePropertyName) + + checkWithOwner := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_name", trueFlasePropertyName), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.#", "1"), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.0", "true"), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: checkWithOwner, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("creates custom property of type string without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-%s" + auto_init = true + } + resource "github_repository_custom_property" "test" { + repository = github_repository.test.name + property_name = "%s" + property_type = "string" + property_value = ["text"] + } + `, randomID, stringPropertyName) + + checkWithOwner := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_name", stringPropertyName), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.#", "1"), + resource.TestCheckResourceAttr("github_repository_custom_property.test", "property_value.0", "text"), + ) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: checkWithOwner, + }, + }, + }) + } + + t.Run("with an anonymous account", func(t *testing.T) { + t.Skip("anonymous account not supported for this operation") + }) + + t.Run("with an individual account", func(t *testing.T) { + t.Skip("individual account not supported for this operation") + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) +} diff --git a/website/docs/d/repository_custom_properties.html.markdown b/website/docs/d/repository_custom_properties.html.markdown new file mode 100644 index 0000000000..17677b5f75 --- /dev/null +++ b/website/docs/d/repository_custom_properties.html.markdown @@ -0,0 +1,28 @@ +--- +layout: "github" +page_title: "GitHub: github_repository_custom_properties" +description: |- + Get all custom properties of a repository +--- + +# github_repository_custom_properties + +Use this data source to retrieve all custom properties of a repository. + +## Example Usage + +```hcl +data "github_repository_custom_properties" "example" { + repository = "example-repository" +} +``` + +## Argument Reference + +* `repository` - (Required) Name of the repository to retrieve the custom properties from. + +## Attributes Reference + +* `property` - The list of this repository's custom properties. Each element of `property` has the following attributes: + * `property_name` - Name of the property + * `property_value` - Value of the property diff --git a/website/docs/r/repository_custom_property.html.markdown b/website/docs/r/repository_custom_property.html.markdown new file mode 100644 index 0000000000..382dc52d44 --- /dev/null +++ b/website/docs/r/repository_custom_property.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "github" +page_title: "GitHub: github_repository_custom_property" +description: |- + Creates and a specific custom property for a GitHub repository +--- + +# github_repository_custom_property + +This resource allows you to create and manage a specific custom property for a GitHub repository. + +## Example Usage + +> Note that this assumes there already is a custom property defined on the org level called `my-cool-property` of type `string` + +```hcl +resource "github_repository" "example" { + name = "example" + description = "My awesome codebase" +} +resource "github_repository_custom_property" "string" { + repository = github_repository.example.name + property_name = "my-cool-property" + property_type = "string" + property_value = ["test"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `repository` - (Required) The repository of the environment. + +* `property_type` - (Required) Type of the custom property. Can be one of `single_select`, `multi_select`, `string`, or `true_false` + +* `property_name` - (Required) Name of the custom property. Note that a pre-requisiste for this resource is that a custom property of this name has already been defined on the organization level + +* `property_value` - (Required) Value of the custom property in the form of an array. Properties of type `single_select`, `string`, and `true_false` are represented as a string array of length 1 + +## Import + +GitHub Repository Custom Property can be imported using an ID made up of a comibnation of the names of the organization, repository, custom property separated by a `:` character, e.g. + +``` +$ terraform import github_repository_custom_property.example :: +```