Skip to content

Commit 76e4f34

Browse files
committed
Add state migration for missing branch field
Signed-off-by: Timo Sand <[email protected]>
1 parent 6b2938f commit 76e4f34

3 files changed

Lines changed: 235 additions & 0 deletions

File tree

github/resource_github_repository_file.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ func resourceGithubRepositoryFile() *schema.Resource {
2424
StateContext: resourceGithubRepositoryFileImport,
2525
},
2626

27+
SchemaVersion: 1,
28+
StateUpgraders: []schema.StateUpgrader{
29+
{
30+
Type: resourceGithubRepositoryFileV0().CoreConfigSchema().ImpliedType(),
31+
Upgrade: resourceGithubRepositoryFileStateUpgradeV0,
32+
Version: 0,
33+
},
34+
},
35+
2736
Description: "This resource allows you to create and manage files within a GitHub repository.",
2837

2938
Schema: map[string]*schema.Schema{
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-log/tflog"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
)
10+
11+
func resourceGithubRepositoryFileV0() *schema.Resource {
12+
return &schema.Resource{
13+
Schema: map[string]*schema.Schema{
14+
"repository": {
15+
Type: schema.TypeString,
16+
Required: true,
17+
ForceNew: true,
18+
},
19+
"file": {
20+
Type: schema.TypeString,
21+
Required: true,
22+
ForceNew: true,
23+
},
24+
"content": {
25+
Type: schema.TypeString,
26+
Required: true,
27+
},
28+
"branch": {
29+
Type: schema.TypeString,
30+
Optional: true,
31+
ForceNew: true,
32+
},
33+
"ref": {
34+
Type: schema.TypeString,
35+
Computed: true,
36+
ForceNew: true,
37+
},
38+
"commit_sha": {
39+
Type: schema.TypeString,
40+
Computed: true,
41+
},
42+
"commit_message": {
43+
Type: schema.TypeString,
44+
Optional: true,
45+
Computed: true,
46+
},
47+
"commit_author": {
48+
Type: schema.TypeString,
49+
Optional: true,
50+
RequiredWith: []string{"commit_email"},
51+
},
52+
"commit_email": {
53+
Type: schema.TypeString,
54+
Optional: true,
55+
RequiredWith: []string{"commit_author"},
56+
},
57+
"sha": {
58+
Type: schema.TypeString,
59+
Computed: true,
60+
},
61+
"overwrite_on_create": {
62+
Type: schema.TypeBool,
63+
Optional: true,
64+
Default: false,
65+
},
66+
"autocreate_branch": {
67+
Type: schema.TypeBool,
68+
Optional: true,
69+
Default: false,
70+
},
71+
"autocreate_branch_source_branch": {
72+
Type: schema.TypeString,
73+
Default: "main",
74+
Optional: true,
75+
RequiredWith: []string{"autocreate_branch"},
76+
},
77+
"autocreate_branch_source_sha": {
78+
Type: schema.TypeString,
79+
Optional: true,
80+
Computed: true,
81+
RequiredWith: []string{"autocreate_branch"},
82+
},
83+
},
84+
}
85+
}
86+
87+
func resourceGithubRepositoryFileStateUpgradeV0(ctx context.Context, rawState map[string]any, m any) (map[string]any, error) {
88+
tflog.Debug(ctx, "GitHub Repository File State before migration", map[string]any{
89+
"rawState": rawState,
90+
})
91+
92+
// If branch is missing or empty, fetch the default branch from the repository
93+
if branch, ok := rawState["branch"].(string); !ok || branch == "" {
94+
meta := m.(*Owner)
95+
client := meta.v3client
96+
owner := meta.name
97+
98+
repoName, ok := rawState["repository"].(string)
99+
if !ok {
100+
return nil, fmt.Errorf("repository not found or is not a string")
101+
}
102+
103+
repo, _, err := client.Repositories.Get(ctx, owner, repoName)
104+
if err != nil {
105+
return nil, fmt.Errorf("failed to retrieve repository %s: %w", repoName, err)
106+
}
107+
108+
rawState["branch"] = repo.GetDefaultBranch()
109+
}
110+
111+
tflog.Debug(ctx, "GitHub Repository File State after migration", map[string]any{
112+
"rawState": rawState,
113+
})
114+
return rawState, nil
115+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"reflect"
6+
"testing"
7+
)
8+
9+
func Test_resourceGithubRepositoryFileStateUpgradeV0(t *testing.T) {
10+
t.Parallel()
11+
12+
for _, d := range []struct {
13+
testName string
14+
rawState map[string]any
15+
want map[string]any
16+
shouldError bool
17+
}{
18+
{
19+
testName: "preserves_existing_branch",
20+
rawState: map[string]any{
21+
"id": "test-repo/path/to/file.txt",
22+
"repository": "test-repo",
23+
"file": "path/to/file.txt",
24+
"content": "file content",
25+
"branch": "main",
26+
"commit_sha": "abc123",
27+
"sha": "def456",
28+
"overwrite_on_create": false,
29+
},
30+
want: map[string]any{
31+
"id": "test-repo/path/to/file.txt",
32+
"repository": "test-repo",
33+
"file": "path/to/file.txt",
34+
"content": "file content",
35+
"branch": "main",
36+
"commit_sha": "abc123",
37+
"sha": "def456",
38+
"overwrite_on_create": false,
39+
},
40+
shouldError: false,
41+
},
42+
{
43+
testName: "preserves_custom_branch",
44+
rawState: map[string]any{
45+
"id": "test-repo/README.md",
46+
"repository": "test-repo",
47+
"file": "README.md",
48+
"content": "# README",
49+
"branch": "develop",
50+
},
51+
want: map[string]any{
52+
"id": "test-repo/README.md",
53+
"repository": "test-repo",
54+
"file": "README.md",
55+
"content": "# README",
56+
"branch": "develop",
57+
},
58+
shouldError: false,
59+
},
60+
// TODO: Enable this test once we have a pattern to create a mock client for the test.
61+
// {
62+
// testName: "migrates_with_missing_branch",
63+
// rawState: map[string]any{
64+
// "id": "test-repo/path/to/file.txt",
65+
// "repository": "test-repo",
66+
// "file": "path/to/file.txt",
67+
// "content": "file content",
68+
// },
69+
// want: map[string]any{
70+
// "id": "test-repo/path/to/file.txt",
71+
// "repository": "test-repo",
72+
// "file": "path/to/file.txt",
73+
// "content": "file content",
74+
// "branch": "main", // fetched from API
75+
// },
76+
// shouldError: false,
77+
// },
78+
// TODO: Enable this test once we have a pattern to create a mock client for the test.
79+
// {
80+
// testName: "migrates_with_empty_branch",
81+
// rawState: map[string]any{
82+
// "id": "test-repo/path/to/file.txt",
83+
// "repository": "test-repo",
84+
// "file": "path/to/file.txt",
85+
// "content": "file content",
86+
// "branch": "",
87+
// },
88+
// want: map[string]any{
89+
// "id": "test-repo/path/to/file.txt",
90+
// "repository": "test-repo",
91+
// "file": "path/to/file.txt",
92+
// "content": "file content",
93+
// "branch": "main", // fetched from API
94+
// },
95+
// shouldError: false,
96+
// },
97+
} {
98+
t.Run(d.testName, func(t *testing.T) {
99+
t.Parallel()
100+
101+
got, err := resourceGithubRepositoryFileStateUpgradeV0(context.Background(), d.rawState, nil)
102+
if (err != nil) != d.shouldError {
103+
t.Fatalf("unexpected error state: got error %v, shouldError %v", err, d.shouldError)
104+
}
105+
106+
if !d.shouldError && !reflect.DeepEqual(got, d.want) {
107+
t.Fatalf("got %+v, want %+v", got, d.want)
108+
}
109+
})
110+
}
111+
}

0 commit comments

Comments
 (0)