Skip to content

Commit 470c1c4

Browse files
authored
[feat]: implement github_codespaces_organization_secret_repositories resource
2 parents b7e63d6 + 0ccf814 commit 470c1c4

6 files changed

+245
-1
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ export TF_LOG=DEBUG
180180
# enable testing of organization scenarios instead of individual or anonymous
181181
export GITHUB_ORGANIZATION=
182182

183-
# enable testing of individual scenarios instead of organizaiton or anonymous
183+
# enable testing of individual scenarios instead of organization or anonymous
184184
export GITHUB_OWNER=
185185

186186
# enable testing of enterprise appliances

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func Provider() terraform.ResourceProvider {
115115
"github_branch_protection": resourceGithubBranchProtection(),
116116
"github_branch_protection_v3": resourceGithubBranchProtectionV3(),
117117
"github_codespaces_organization_secret": resourceGithubCodespacesOrganizationSecret(),
118+
"github_codespaces_organization_secret_repositories": resourceGithubCodespacesOrganizationSecretRepositories(),
118119
"github_codespaces_secret": resourceGithubCodespacesSecret(),
119120
"github_codespaces_user_secret": resourceGithubCodespacesUserSecret(),
120121
"github_dependabot_organization_secret": resourceGithubDependabotOrganizationSecret(),
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package github
2+
3+
import (
4+
"context"
5+
6+
"github.com/google/go-github/v53/github"
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
8+
)
9+
10+
func resourceGithubCodespacesOrganizationSecretRepositories() *schema.Resource {
11+
return &schema.Resource{
12+
Create: resourceGithubCodespaceOrganizationSecretRepositoriesCreateOrUpdate,
13+
Read: resourceGithubCodespaceOrganizationSecretRepositoriesRead,
14+
Update: resourceGithubCodespaceOrganizationSecretRepositoriesCreateOrUpdate,
15+
Delete: resourceGithubCodespaceOrganizationSecretRepositoriesDelete,
16+
Importer: &schema.ResourceImporter{
17+
State: schema.ImportStatePassthrough,
18+
},
19+
20+
Schema: map[string]*schema.Schema{
21+
"secret_name": {
22+
Type: schema.TypeString,
23+
Required: true,
24+
ForceNew: true,
25+
Description: "Name of the existing secret.",
26+
ValidateFunc: validateSecretNameFunc,
27+
},
28+
"selected_repository_ids": {
29+
Type: schema.TypeSet,
30+
Elem: &schema.Schema{
31+
Type: schema.TypeInt,
32+
},
33+
Set: schema.HashInt,
34+
Required: true,
35+
Description: "An array of repository ids that can access the organization secret.",
36+
},
37+
},
38+
}
39+
}
40+
41+
func resourceGithubCodespaceOrganizationSecretRepositoriesCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
42+
client := meta.(*Owner).v3client
43+
owner := meta.(*Owner).name
44+
ctx := context.Background()
45+
46+
err := checkOrganization(meta)
47+
if err != nil {
48+
return err
49+
}
50+
51+
secretName := d.Get("secret_name").(string)
52+
selectedRepositories := d.Get("selected_repository_ids")
53+
54+
selectedRepositoryIDs := []int64{}
55+
56+
ids := selectedRepositories.(*schema.Set).List()
57+
for _, id := range ids {
58+
selectedRepositoryIDs = append(selectedRepositoryIDs, int64(id.(int)))
59+
}
60+
61+
_, err = client.Codespaces.SetSelectedReposForOrgSecret(ctx, owner, secretName, selectedRepositoryIDs)
62+
if err != nil {
63+
return err
64+
}
65+
66+
d.SetId(secretName)
67+
return resourceGithubCodespaceOrganizationSecretRepositoriesRead(d, meta)
68+
}
69+
70+
func resourceGithubCodespaceOrganizationSecretRepositoriesRead(d *schema.ResourceData, meta interface{}) error {
71+
client := meta.(*Owner).v3client
72+
owner := meta.(*Owner).name
73+
ctx := context.Background()
74+
75+
err := checkOrganization(meta)
76+
if err != nil {
77+
return err
78+
}
79+
80+
selectedRepositoryIDs := github.SelectedRepoIDs{}
81+
opt := &github.ListOptions{
82+
PerPage: maxPerPage,
83+
}
84+
for {
85+
results, resp, err := client.Codespaces.ListSelectedReposForOrgSecret(ctx, owner, d.Id(), opt)
86+
if err != nil {
87+
return err
88+
}
89+
90+
for _, repo := range results.Repositories {
91+
selectedRepositoryIDs = append(selectedRepositoryIDs, repo.GetID())
92+
}
93+
94+
if resp.NextPage == 0 {
95+
break
96+
}
97+
opt.Page = resp.NextPage
98+
}
99+
100+
d.Set("selected_repository_ids", selectedRepositoryIDs)
101+
102+
return nil
103+
}
104+
105+
func resourceGithubCodespaceOrganizationSecretRepositoriesDelete(d *schema.ResourceData, meta interface{}) error {
106+
client := meta.(*Owner).v3client
107+
owner := meta.(*Owner).name
108+
ctx := context.WithValue(context.Background(), ctxId, d.Id())
109+
110+
err := checkOrganization(meta)
111+
if err != nil {
112+
return err
113+
}
114+
115+
selectedRepositoryIDs := github.SelectedRepoIDs{}
116+
_, err = client.Codespaces.SetSelectedReposForOrgSecret(ctx, owner, d.Id(), selectedRepositoryIDs)
117+
if err != nil {
118+
return err
119+
}
120+
121+
return nil
122+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
10+
)
11+
12+
func TestAccGithubCodespacesOrganizationSecretRepositories(t *testing.T) {
13+
const ORG_SECRET_NAME = "ORG_SECRET_NAME"
14+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
15+
secret_name, exists := os.LookupEnv(ORG_SECRET_NAME)
16+
17+
t.Run("set repository allowlist for an organization secret", func(t *testing.T) {
18+
if !exists {
19+
t.Skipf("%s environment variable is missing", ORG_SECRET_NAME)
20+
}
21+
22+
config := fmt.Sprintf(`
23+
resource "github_repository" "test_repo_1" {
24+
name = "tf-acc-test-%s-1"
25+
visibility = "internal"
26+
}
27+
28+
resource "github_repository" "test_repo_2" {
29+
name = "tf-acc-test-%s-2"
30+
visibility = "internal"
31+
}
32+
33+
resource "github_codespaces_organization_secret_repositories" "org_secret_repos" {
34+
secret_name = "%s"
35+
selected_repository_ids = [
36+
github_repository.test_repo_1.repo_id,
37+
github_repository.test_repo_2.repo_id
38+
]
39+
}
40+
`, randomID, randomID, secret_name)
41+
42+
check := resource.ComposeTestCheckFunc(
43+
resource.TestCheckResourceAttrSet(
44+
"github_codespaces_organization_secret_repositories.org_secret_repos", "secret_name",
45+
),
46+
resource.TestCheckResourceAttr(
47+
"github_codespaces_organization_secret_repositories.org_secret_repos", "selected_repository_ids.#", "2",
48+
),
49+
)
50+
51+
testCase := func(t *testing.T, mode string) {
52+
resource.Test(t, resource.TestCase{
53+
PreCheck: func() { skipUnlessMode(t, mode) },
54+
Providers: testAccProviders,
55+
Steps: []resource.TestStep{
56+
{
57+
Config: config,
58+
Check: check,
59+
},
60+
},
61+
})
62+
}
63+
64+
t.Run("with an anonymous account", func(t *testing.T) {
65+
t.Skip("anonymous account not supported for this operation")
66+
})
67+
68+
t.Run("with an individual account", func(t *testing.T) {
69+
t.Skip("individual account not supported for this operation")
70+
})
71+
72+
t.Run("with an organization account", func(t *testing.T) {
73+
testCase(t, organization)
74+
})
75+
})
76+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
layout: "github"
3+
page_title: "GitHub: github_codespaces_organization_secret_repositories"
4+
description: |-
5+
Manages repository allow list for a Codespaces Secret within a GitHub organization
6+
---
7+
8+
# github_codespaces_organization_secret_repositories
9+
10+
This resource allows you to manage repository allow list for existing GitHub Codespaces secrets within your GitHub organization.
11+
12+
You must have write access to an organization secret to use this resource.
13+
14+
This resource is only applicable when `visibility` of the existing organization secret has been set to `selected`.
15+
16+
## Example Usage
17+
18+
```hcl
19+
data "github_repository" "repo" {
20+
full_name = "my-org/repo"
21+
}
22+
23+
resource "github_codespaces_organization_secret_repositories" "org_secret_repos" {
24+
secret_name = "existing_secret_name"
25+
selected_repository_ids = [data.github_repository.repo.repo_id]
26+
}
27+
```
28+
29+
## Argument Reference
30+
31+
The following arguments are supported:
32+
33+
* `secret_name` - (Required) Name of the existing secret
34+
* `selected_repository_ids` - (Required) An array of repository ids that can access the organization secret.
35+
36+
## Import
37+
38+
This resource can be imported using an ID made up of the secret name:
39+
40+
```
41+
$ terraform import github_codespaces_organization_secret_repositories.org_secret_repos existing_secret_name
42+
```

website/github.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@
7070
<li>
7171
<a href="/docs/providers/github/d/codespaces_organization_secrets.html">github_codespaces_organization_secrets</a>
7272
</li>
73+
<li>
74+
<a href="/docs/providers/github/d/codespaces_organization_secret_repositories.html">github_codespaces_organization_secret_repositories</a>
75+
</li>
7376
<li>
7477
<a href="/docs/providers/github/d/codespaces_public_key.html">github_codespaces_public_key</a>
7578
</li>

0 commit comments

Comments
 (0)