diff --git a/github/data_source_github_repository.go b/github/data_source_github_repository.go index ed2a30143b..36911e4ca2 100644 --- a/github/data_source_github_repository.go +++ b/github/data_source_github_repository.go @@ -20,7 +20,7 @@ func dataSourceGithubRepository() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ConflictsWith: []string{"name"}, + ConflictsWith: []string{"name", "owner"}, }, "name": { Type: schema.TypeString, @@ -28,6 +28,13 @@ func dataSourceGithubRepository() *schema.Resource { Computed: true, ConflictsWith: []string{"full_name"}, }, + "owner": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"full_name"}, + Description: "Owner of the repository. If not provided, the owner specified in the provider configuration will be used.", + }, "description": { Type: schema.TypeString, Default: nil, @@ -353,6 +360,9 @@ func dataSourceGithubRepositoryRead(d *schema.ResourceData, meta interface{}) er if name, ok := d.GetOk("name"); ok { repoName = name.(string) } + if ownerName, ok := d.GetOk("owner"); ok { + owner = ownerName.(string) + } if repoName == "" { return fmt.Errorf("one of %q or %q has to be provided", "full_name", "name") diff --git a/github/data_source_github_repository_test.go b/github/data_source_github_repository_test.go index 7c362ebf91..5ca538622f 100644 --- a/github/data_source_github_repository_test.go +++ b/github/data_source_github_repository_test.go @@ -445,4 +445,179 @@ EOT }) }) + + t.Run("queries a repository using owner and name", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%s" + } + + data "github_repository" "test" { + name = github_repository.test.name + owner = "%s" + } + `, randomID, testOrganization) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.github_repository.test", "owner", + testOrganization, + ), + ) + + 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) { + testCase(t, anonymous) + }) + + t.Run("with an individual account", func(t *testing.T) { + testCase(t, individual) + }) + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + + t.Run("validates conflicts between full_name, name, and owner", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + vulnerability_alerts = true + } + `, randomID) + + // Test invalid combinations + invalidConfigs := []string{ + // full_name with name + fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + vulnerability_alerts = true + } + + data "github_repository" "test" { + full_name = "%[2]s/tf-acc-%[1]s" + name = "tf-acc-%[1]s" + } + `, randomID, testOrganization), + // full_name with owner + fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + } + + data "github_repository" "test" { + full_name = "%[2]s/tf-acc-%[1]s" + owner = "%[2]s" + } + `, randomID, testOrganization), + // full_name with both name and owner + fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + } + + data "github_repository" "test" { + full_name = "%[2]s/tf-acc-%[1]s" + name = "tf-acc-%[1]s" + owner = "%[2]s" + } + `, randomID, testOrganization), + } + + // Test valid combinations + validConfigs := []string{ + // Just full_name + fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + } + + data "github_repository" "test" { + full_name = "%[2]s/tf-acc-%[1]s" + } + `, randomID, testOrganization), + // Just name (uses provider owner) + fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + } + + data "github_repository" "test" { + name = "tf-acc-%[1]s" + } + `, randomID), + // name with owner + fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-%[1]s" + } + + data "github_repository" "test" { + name = "tf-acc-%[1]s" + owner = "%[2]s" + } + `, randomID, testOrganization), + } + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create the repository first + { + Config: config, + }, + // Test that invalid configs fail + { + Config: invalidConfigs[0], + ExpectError: regexp.MustCompile("(?i)conflicts with"), + }, + { + Config: invalidConfigs[1], + ExpectError: regexp.MustCompile("(?i)conflicts with"), + }, + { + Config: invalidConfigs[2], + ExpectError: regexp.MustCompile("(?i)conflicts with"), + }, + // Test that valid configs succeed + { + Config: validConfigs[0], + }, + { + Config: validConfigs[1], + }, + { + Config: validConfigs[2], + }, + }, + }) + } + + t.Run("with an individual account", func(t *testing.T) { + testCase(t, individual) + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) } diff --git a/github/resource_github_repository.go b/github/resource_github_repository.go index f28cc4c6e8..64479fbbd9 100644 --- a/github/resource_github_repository.go +++ b/github/resource_github_repository.go @@ -332,6 +332,11 @@ func resourceGithubRepository() *schema.Resource { Computed: true, Description: "A string of the form 'orgname/reponame'.", }, + "owner": { + Type: schema.TypeString, + Computed: true, + Description: "The owner of the repository.", + }, "html_url": { Type: schema.TypeString, Computed: true, @@ -678,6 +683,7 @@ func resourceGithubRepositoryRead(d *schema.ResourceData, meta interface{}) erro d.Set("topics", flattenStringList(repo.Topics)) d.Set("node_id", repo.GetNodeID()) d.Set("repo_id", repo.GetID()) + d.Set("owner", repo.GetOwner().GetLogin()) // GitHub API doesn't respond following parameters when repository is archived if !d.Get("archived").(bool) { diff --git a/github/resource_github_repository_test.go b/github/resource_github_repository_test.go index e09505dac9..e098985c6c 100644 --- a/github/resource_github_repository_test.go +++ b/github/resource_github_repository_test.go @@ -985,7 +985,50 @@ func TestAccGithubRepositories(t *testing.T) { }) + t.Run("creates repository and returns owner field", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-owner-%[1]s" + description = "Terraform acceptance tests %[1]s" + } + `, randomID) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "github_repository.test", "owner", + ), + ) + + 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) { + testCase(t, individual) + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + }) + } + func TestAccGithubRepositoryPages(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) diff --git a/website/docs/d/repository.html.markdown b/website/docs/d/repository.html.markdown index 442d1b6d56..83c6d80ffc 100644 --- a/website/docs/d/repository.html.markdown +++ b/website/docs/d/repository.html.markdown @@ -25,6 +25,8 @@ The following arguments are supported: * `full_name` - (Optional) Full name of the repository (in `org/name` format). +* `owner` - (Optional) Owner of the repository. If not provided, the owner specified in the provider configuration will be used. + ## Attributes Reference * `node_id` - the Node ID of the repository. diff --git a/website/docs/r/repository.html.markdown b/website/docs/r/repository.html.markdown index dc18add1d6..7501c5c8aa 100644 --- a/website/docs/r/repository.html.markdown +++ b/website/docs/r/repository.html.markdown @@ -182,6 +182,8 @@ The following additional attributes are exported: * `full_name` - A string of the form "orgname/reponame". +* `owner` - The owner of the repository. + * `html_url` - URL to the repository on the web. * `ssh_clone_url` - URL that can be provided to `git clone` to clone the repository via SSH.