Skip to content

Commit 9870fc6

Browse files
authored
fix: Refactor repository custom property (#3476)
* fix: Refactor repository custom property Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com> * fixup! fix: Refactor repository custom property * fixup! fix: Refactor repository custom property * fixup! fix: Refactor repository custom property * fixup! fix: Refactor repository custom property * fixup! fix: Refactor repository custom property * fixup! fix: Refactor repository custom property --------- Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com>
1 parent d8e5d0a commit 9870fc6

18 files changed

Lines changed: 912 additions & 236 deletions

ARCHITECTURE.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ func resourceGithubExample() *schema.Resource {
178178
StateContext: resourceGithubExampleImport,
179179
},
180180

181-
// Include SchemaVersion and StateUpgraders if state migrations exist
181+
// Only if required.
182+
CustomizeDiff: diffExample,
183+
184+
// Include SchemaVersion and StateUpgraders if state migrations exist.
182185
SchemaVersion: 1,
183186
StateUpgraders: []schema.StateUpgrader{
184187
{
@@ -188,8 +191,10 @@ func resourceGithubExample() *schema.Resource {
188191
},
189192
},
190193

194+
Description: "Manages an example GitHub resource.",
195+
191196
Schema: map[string]*schema.Schema{
192-
// Schema definition
197+
// Schema definition.
193198
},
194199
}
195200
}

docs/resources/repository_custom_property.md

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,62 @@
11
---
22
page_title: "github_repository_custom_property (Resource) - GitHub"
3+
subcategory: ""
34
description: |-
4-
Creates and a specific custom property for a GitHub repository
5+
Resource to manage GitHub repository custom properties.
56
---
67

78
# github_repository_custom_property (Resource)
89

9-
This resource allows you to create and manage a specific custom property for a GitHub repository.
10+
Resource to manage GitHub repository custom properties.
11+
For more information, see the [GitHub API documentation](https://docs.github.com/rest/metadata/custom-properties#create-or-update-repository-custom-property).
1012

1113
## Example Usage
1214

13-
> Note that this assumes there already is a custom property defined on the org level called `my-cool-property` of type `string`
14-
1515
```terraform
16+
# NOTE: This assumes there already is a custom property defined on the org level called `my-cool-string` of type `string`
17+
1618
resource "github_repository" "example" {
1719
name = "example"
1820
description = "My awesome codebase"
1921
}
20-
resource "github_repository_custom_property" "string" {
22+
23+
resource "github_repository_custom_property" "example" {
2124
repository = github_repository.example.name
22-
property_name = "my-cool-property"
25+
property_name = "my-cool-string"
2326
property_type = "string"
2427
property_value = ["test"]
2528
}
2629
```
2730

28-
## Argument Reference
31+
<!-- schema generated by tfplugindocs -->
32+
## Schema
2933

30-
The following arguments are supported:
34+
### Required
3135

32-
- `repository` - (Required) The repository of the environment.
36+
- `property_name` (String) Name of the custom property.
37+
- `property_type` (String) Type of the custom property. Valid values are `string`, `single_select`, `multi_select`, `true_false`, and `url`.
38+
- `property_value` (Set of String) Value of the custom property. For `string`, `single_select`, `true_false`, and `url` property types, this should be a single value. For `multi_select` property types, this can be multiple values.
39+
- `repository` (String) Name of the repository.
3340

34-
- `property_type` - (Required) Type of the custom property. Can be one of `single_select`, `multi_select`, `string`, or `true_false`
41+
### Read-Only
3542

36-
- `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
37-
38-
- `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
43+
- `id` (String) The ID of this resource.
44+
- `repository_id` (Number) ID of the repository.
3945

4046
## Import
4147

42-
GitHub Repository Custom Property can be imported using an ID made up of a combination of the names of the organization, repository, custom property separated by a `:` character, e.g.
48+
Import is supported using the following syntax:
49+
50+
In Terraform v1.5.0 and later, the [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used with the `id` attribute, for example:
51+
52+
```terraform
53+
import {
54+
to = github_repository_custom_property.example
55+
id = "organization-name:repo-name:custom-property-name"
56+
}
57+
```
58+
59+
The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example:
4360

4461
```shell
4562
terraform import github_repository_custom_property.example organization-name:repo-name:custom-property-name
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import {
2+
to = github_repository_custom_property.example
3+
id = "organization-name:repo-name:custom-property-name"
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terraform import github_repository_custom_property.example organization-name:repo-name:custom-property-name
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# NOTE: This assumes there already is a custom property defined on the org level called `my-cool-string` of type `string`
2+
3+
resource "github_repository" "example" {
4+
name = "example"
5+
description = "My awesome codebase"
6+
}
7+
8+
resource "github_repository_custom_property" "example" {
9+
repository = github_repository.example.name
10+
property_name = "my-cool-string"
11+
property_type = "string"
12+
property_value = ["test"]
13+
}

examples/resources/repository_custom_property/example_1.tf

Lines changed: 0 additions & 10 deletions
This file was deleted.

github/acc_helpers_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"testing"
9+
10+
"github.com/google/go-github/v88/github"
11+
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
12+
)
13+
14+
const testRandomIDLength = 5
15+
16+
func mustGetTestMockResponse(t *testing.T, uri string, statusCode int, body any) *mockResponse {
17+
resp := &mockResponse{
18+
ExpectedUri: uri,
19+
StatusCode: statusCode,
20+
}
21+
22+
if body != nil {
23+
bodyBytes, err := json.Marshal(body)
24+
if err != nil {
25+
t.Fatalf("failed to marshal mock response body: %v", err)
26+
}
27+
resp.ResponseBody = string(bodyBytes)
28+
}
29+
30+
return resp
31+
}
32+
33+
func mustCreateTestGitHubClient(t *testing.T, baseURL string, opts ...github.ClientOptionsFunc) *github.Client {
34+
client, err := github.NewClient(append([]github.ClientOptionsFunc{github.WithURLs(&baseURL, nil)}, opts...)...)
35+
if err != nil {
36+
t.Fatalf("failed to create GitHub client: %s", err)
37+
}
38+
return client
39+
}
40+
41+
func mustCreateTestOrganizationRepositoryCustomProperty(t *testing.T, valType string, allowed []string) *github.CustomProperty {
42+
t.Helper()
43+
44+
meta, err := getTestMeta()
45+
if err != nil {
46+
t.Fatalf("failed to get test meta: %v", err)
47+
}
48+
49+
randomID := acctest.RandString(testRandomIDLength)
50+
name := fmt.Sprintf("%s%s", testResourcePrefix, randomID)
51+
52+
req := &github.CustomProperty{
53+
PropertyName: &name,
54+
ValueType: github.PropertyValueType(valType),
55+
AllowedValues: allowed,
56+
}
57+
58+
prop, _, err := meta.v3client.Organizations.CreateOrUpdateCustomProperty(t.Context(), meta.name, name, req)
59+
if err != nil {
60+
t.Fatalf("failed to create test organization repository custom property: %v", err)
61+
}
62+
63+
t.Cleanup(func() {
64+
if _, err := meta.v3client.Organizations.RemoveCustomProperty(context.Background(), meta.name, name); err != nil {
65+
if err, ok := errors.AsType[*github.ErrorResponse](err); ok && err.Response.StatusCode == 404 {
66+
return
67+
}
68+
t.Logf("failed to delete test organization repository custom property %s: %v", name, err)
69+
}
70+
})
71+
72+
return prop
73+
}
74+
75+
func mustCreateTestRepository(t *testing.T) *github.Repository {
76+
t.Helper()
77+
78+
meta, err := getTestMeta()
79+
if err != nil {
80+
t.Fatalf("failed to get test meta: %v", err)
81+
}
82+
83+
randomID := acctest.RandString(testRandomIDLength)
84+
name := fmt.Sprintf("%s%s", testResourcePrefix, randomID)
85+
86+
req := &github.Repository{
87+
Name: &name,
88+
AutoInit: new(true),
89+
}
90+
91+
repo, _, err := meta.v3client.Repositories.Create(t.Context(), meta.name, req)
92+
if err != nil {
93+
t.Fatalf("failed to create test repository: %v", err)
94+
}
95+
96+
t.Cleanup(func() {
97+
if _, err := meta.v3client.Repositories.Delete(context.Background(), meta.name, name); err != nil {
98+
if err, ok := errors.AsType[*github.ErrorResponse](err); ok && err.Response.StatusCode == 404 {
99+
return
100+
}
101+
t.Logf("failed to delete test repository %s: %v", name, err)
102+
}
103+
})
104+
105+
return repo
106+
}

github/data_source_github_app_token_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestAccGithubAppTokenDataSource(t *testing.T) {
3232
})
3333
defer ts.Close()
3434

35-
client := mustGitHubClient(t, ts.URL)
35+
client := mustCreateTestGitHubClient(t, ts.URL)
3636

3737
meta := &Owner{
3838
name: owner,

github/helpers_test.go

Lines changed: 0 additions & 15 deletions
This file was deleted.

github/migration.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/go-github/v88/github"
8+
"github.com/hashicorp/terraform-plugin-log/tflog"
9+
)
10+
11+
// migrateRepositoryWithID is a helper function to migrate a raw state where the repository is set to make usre that the repository_id is added to the state.
12+
func migrateRepositoryWithID(ctx context.Context, client *github.Client, owner string, rawState map[string]any) (map[string]any, error) {
13+
tflog.Debug(ctx, "Migrating state to add repository_id.")
14+
15+
repoNameVal, ok := rawState["repository"]
16+
if !ok {
17+
return nil, fmt.Errorf("repository name not found in state")
18+
}
19+
20+
repoName, ok := repoNameVal.(string)
21+
if !ok {
22+
return nil, fmt.Errorf("repository name is not a string")
23+
}
24+
25+
if repoID, ok := rawState["repository_id"]; ok {
26+
if _, ok := repoID.(int); ok {
27+
tflog.Debug(ctx, "Found repository_id in state, skipping migration.", map[string]any{"repository": repoName, "repository_id": repoID})
28+
return rawState, nil
29+
}
30+
}
31+
32+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to retrieve repository %s: %w", repoName, err)
35+
}
36+
37+
repoID := int(repo.GetID())
38+
rawState["repository_id"] = repoID
39+
40+
tflog.Debug(ctx, "State migrated to add repository_id.", map[string]any{"repository": repoName, "repository_id": repoID})
41+
42+
return rawState, nil
43+
}

0 commit comments

Comments
 (0)