Skip to content

Commit 7daca1e

Browse files
committed
Adding a computed attribute for the repository owner in the github_repository resource and data source. Fixes #2503
1 parent 46682cf commit 7daca1e

6 files changed

Lines changed: 246 additions & 1 deletion

github/data_source_github_repository.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,21 @@ func dataSourceGithubRepository() *schema.Resource {
2222
Type: schema.TypeString,
2323
Optional: true,
2424
Computed: true,
25-
ConflictsWith: []string{"name"},
25+
ConflictsWith: []string{"name", "owner"},
2626
},
2727
"name": {
2828
Type: schema.TypeString,
2929
Optional: true,
3030
Computed: true,
3131
ConflictsWith: []string{"full_name"},
3232
},
33+
"owner": {
34+
Type: schema.TypeString,
35+
Optional: true,
36+
Computed: true,
37+
ConflictsWith: []string{"full_name"},
38+
Description: "Owner of the repository. If not provided, the provider's default owner is used.",
39+
},
3340
"description": {
3441
Type: schema.TypeString,
3542
Default: nil,
@@ -360,6 +367,9 @@ func dataSourceGithubRepositoryRead(ctx context.Context, d *schema.ResourceData,
360367
if name, ok := d.GetOk("name"); ok {
361368
repoName = name.(string)
362369
}
370+
if ownerName, ok := d.GetOk("owner"); ok {
371+
owner = ownerName.(string)
372+
}
363373

364374
if repoName == "" {
365375
return diag.Errorf("one of %q or %q has to be provided", "full_name", "name")

github/data_source_github_repository_test.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,4 +379,179 @@ EOT
379379
},
380380
})
381381
})
382+
383+
t.Run("queries a repository using owner and name", func(t *testing.T) {
384+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
385+
386+
config := fmt.Sprintf(`
387+
resource "github_repository" "test" {
388+
name = "tf-acc-%s"
389+
}
390+
391+
data "github_repository" "test" {
392+
name = github_repository.test.name
393+
owner = "%s"
394+
}
395+
`, randomID, testOrganization)
396+
397+
check := resource.ComposeTestCheckFunc(
398+
resource.TestCheckResourceAttr(
399+
"data.github_repository.test", "owner",
400+
testOrganization,
401+
),
402+
)
403+
404+
testCase := func(t *testing.T, mode string) {
405+
resource.Test(t, resource.TestCase{
406+
PreCheck: func() { skipUnlessMode(t, mode) },
407+
Providers: testAccProviders,
408+
Steps: []resource.TestStep{
409+
{
410+
Config: config,
411+
Check: check,
412+
},
413+
},
414+
})
415+
}
416+
417+
t.Run("with an anonymous account", func(t *testing.T) {
418+
testCase(t, anonymous)
419+
})
420+
421+
t.Run("with an individual account", func(t *testing.T) {
422+
testCase(t, individual)
423+
})
424+
t.Run("with an organization account", func(t *testing.T) {
425+
testCase(t, organization)
426+
})
427+
})
428+
429+
t.Run("validates conflicts between full_name, name, and owner", func(t *testing.T) {
430+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
431+
432+
config := fmt.Sprintf(`
433+
resource "github_repository" "test" {
434+
name = "tf-acc-%[1]s"
435+
vulnerability_alerts = true
436+
}
437+
`, randomID)
438+
439+
// Test invalid combinations
440+
invalidConfigs := []string{
441+
// full_name with name
442+
fmt.Sprintf(`
443+
resource "github_repository" "test" {
444+
name = "tf-acc-%[1]s"
445+
vulnerability_alerts = true
446+
}
447+
448+
data "github_repository" "test" {
449+
full_name = "%[2]s/tf-acc-%[1]s"
450+
name = "tf-acc-%[1]s"
451+
}
452+
`, randomID, testOrganization),
453+
// full_name with owner
454+
fmt.Sprintf(`
455+
resource "github_repository" "test" {
456+
name = "tf-acc-%[1]s"
457+
}
458+
459+
data "github_repository" "test" {
460+
full_name = "%[2]s/tf-acc-%[1]s"
461+
owner = "%[2]s"
462+
}
463+
`, randomID, testOrganization),
464+
// full_name with both name and owner
465+
fmt.Sprintf(`
466+
resource "github_repository" "test" {
467+
name = "tf-acc-%[1]s"
468+
}
469+
470+
data "github_repository" "test" {
471+
full_name = "%[2]s/tf-acc-%[1]s"
472+
name = "tf-acc-%[1]s"
473+
owner = "%[2]s"
474+
}
475+
`, randomID, testOrganization),
476+
}
477+
478+
// Test valid combinations
479+
validConfigs := []string{
480+
// Just full_name
481+
fmt.Sprintf(`
482+
resource "github_repository" "test" {
483+
name = "tf-acc-%[1]s"
484+
}
485+
486+
data "github_repository" "test" {
487+
full_name = "%[2]s/tf-acc-%[1]s"
488+
}
489+
`, randomID, testOrganization),
490+
// Just name (uses provider owner)
491+
fmt.Sprintf(`
492+
resource "github_repository" "test" {
493+
name = "tf-acc-%[1]s"
494+
}
495+
496+
data "github_repository" "test" {
497+
name = "tf-acc-%[1]s"
498+
}
499+
`, randomID),
500+
// name with owner
501+
fmt.Sprintf(`
502+
resource "github_repository" "test" {
503+
name = "tf-acc-%[1]s"
504+
}
505+
506+
data "github_repository" "test" {
507+
name = "tf-acc-%[1]s"
508+
owner = "%[2]s"
509+
}
510+
`, randomID, testOrganization),
511+
}
512+
513+
testCase := func(t *testing.T, mode string) {
514+
resource.Test(t, resource.TestCase{
515+
PreCheck: func() { skipUnlessMode(t, mode) },
516+
Providers: testAccProviders,
517+
Steps: []resource.TestStep{
518+
// Create the repository first
519+
{
520+
Config: config,
521+
},
522+
// Test that invalid configs fail
523+
{
524+
Config: invalidConfigs[0],
525+
ExpectError: regexp.MustCompile("(?i)conflicts with"),
526+
},
527+
{
528+
Config: invalidConfigs[1],
529+
ExpectError: regexp.MustCompile("(?i)conflicts with"),
530+
},
531+
{
532+
Config: invalidConfigs[2],
533+
ExpectError: regexp.MustCompile("(?i)conflicts with"),
534+
},
535+
// Test that valid configs succeed
536+
{
537+
Config: validConfigs[0],
538+
},
539+
{
540+
Config: validConfigs[1],
541+
},
542+
{
543+
Config: validConfigs[2],
544+
},
545+
},
546+
})
547+
}
548+
549+
t.Run("with an individual account", func(t *testing.T) {
550+
testCase(t, individual)
551+
})
552+
553+
t.Run("with an organization account", func(t *testing.T) {
554+
testCase(t, organization)
555+
})
556+
})
382557
}

github/resource_github_repository.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,11 @@ func resourceGithubRepository() *schema.Resource {
412412
Computed: true,
413413
Description: "A string of the form 'orgname/reponame'.",
414414
},
415+
"owner": {
416+
Type: schema.TypeString,
417+
Computed: true,
418+
Description: "The owner of the repository.",
419+
},
415420
"html_url": {
416421
Type: schema.TypeString,
417422
Computed: true,
@@ -832,6 +837,9 @@ func resourceGithubRepositoryRead(ctx context.Context, d *schema.ResourceData, m
832837
_ = d.Set("topics", flattenStringList(repo.Topics))
833838
_ = d.Set("node_id", repo.GetNodeID())
834839
_ = d.Set("repo_id", repo.GetID())
840+
if repo.Owner != nil {
841+
_ = d.Set("owner", repo.Owner.GetLogin())
842+
}
835843

836844
// TODO: Validate this behavior as I can see these fields being returned even when archived
837845
// GitHub API doesn't respond following parameters when repository is archived

github/resource_github_repository_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,53 @@ resource "github_repository" "test" {
894894
// })
895895
// })
896896

897+
t.Run("creates repository and returns owner field", func(t *testing.T) {
898+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
899+
900+
config := fmt.Sprintf(`
901+
resource "github_repository" "test" {
902+
name = "tf-acc-test-owner-%[1]s"
903+
description = "Terraform acceptance tests %[1]s"
904+
}
905+
`, randomID)
906+
907+
check := resource.ComposeTestCheckFunc(
908+
resource.TestCheckResourceAttrSet(
909+
"github_repository.test", "owner",
910+
),
911+
)
912+
913+
testCase := func(t *testing.T, mode string) {
914+
resource.Test(t, resource.TestCase{
915+
PreCheck: func() { skipUnlessMode(t, mode) },
916+
Providers: testAccProviders,
917+
Steps: []resource.TestStep{
918+
{
919+
Config: config,
920+
Check: check,
921+
},
922+
},
923+
})
924+
}
925+
926+
t.Run("with an anonymous account", func(t *testing.T) {
927+
t.Skip("anonymous account not supported for this operation")
928+
})
929+
930+
t.Run("with an individual account", func(t *testing.T) {
931+
testCase(t, individual)
932+
})
933+
934+
t.Run("with an organization account", func(t *testing.T) {
935+
testCase(t, organization)
936+
})
937+
})
938+
939+
}
940+
941+
func TestAccGithubRepositoryPages(t *testing.T) {
942+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
943+
897944
t.Run("manages the legacy pages feature for a repository", func(t *testing.T) {
898945
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
899946
testRepoName := fmt.Sprintf("%slegacy-pages-%s", testResourcePrefix, randomID)

website/docs/d/repository.html.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ The following arguments are supported:
2525

2626
* `full_name` - (Optional) Full name of the repository (in `org/name` format).
2727

28+
* `owner` - (Optional) Owner of the repository. If not provided, the provider's default owner is used.
29+
2830
## Attributes Reference
2931

3032
* `node_id` - the Node ID of the repository.

website/docs/r/repository.html.markdown

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ The following additional attributes are exported:
222222

223223
* `full_name` - A string of the form "orgname/reponame".
224224

225+
* `owner` - The owner of the repository.
226+
225227
* `html_url` - URL to the repository on the web.
226228

227229
* `ssh_clone_url` - URL that can be provided to `git clone` to clone the repository via SSH.
@@ -250,3 +252,4 @@ Repositories can be imported using the `name`, e.g.
250252
```shell
251253
terraform import github_repository.terraform myrepo
252254
```
255+

0 commit comments

Comments
 (0)