Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/repository: add create from template #309

Merged
merged 3 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 77 additions & 7 deletions github/resource_github_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package github

import (
"context"
"errors"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -137,6 +138,25 @@ func resourceGithubRepository() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"template": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
jcudit marked this conversation as resolved.
Show resolved Hide resolved
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"owner": {
Type: schema.TypeString,
Optional: true,
Default: false,
},
"repository": {
Type: schema.TypeString,
Optional: true,
Default: false,
jcudit marked this conversation as resolved.
Show resolved Hide resolved
},
},
},
},
},
}
}
Expand Down Expand Up @@ -174,20 +194,58 @@ func resourceGithubRepositoryCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Cannot set the default branch on a new repository to something other than 'master'.")
}

orgName := meta.(*Organization).name
repoReq := resourceGithubRepositoryObject(d)
orgName := meta.(*Organization).name
repoName := repoReq.GetName()
ctx := context.Background()

log.Printf("[DEBUG] Creating repository: %s/%s", orgName, repoReq.GetName())
repo, _, err := client.Repositories.Create(ctx, orgName, repoReq)
if err != nil {
return err
log.Printf("[DEBUG] Creating repository: %s/%s", orgName, repoName)

if template, ok := d.GetOk("template"); ok {
templateConfigBlocks := template.([]interface{})
if len(templateConfigBlocks) > 1 {
return errors.New("cannot specify template more than once")
}
jcudit marked this conversation as resolved.
Show resolved Hide resolved

for _, templateConfigBlock := range templateConfigBlocks {
templateConfigMap, ok := templateConfigBlock.(map[string]interface{})
if !ok {
return errors.New("failed to unpack template configuration block")
}

templateRepo := templateConfigMap["repository"].(string)
templateRepoOwner := templateConfigMap["owner"].(string)
templateRepoReq := github.TemplateRepoRequest{
Name: &repoName,
Owner: &orgName,
Description: github.String(d.Get("description").(string)),
Private: github.Bool(d.Get("private").(bool)),
}

repo, _, err := client.Repositories.CreateFromTemplate(ctx,
templateRepoOwner,
templateRepo,
&templateRepoReq,
)

if err != nil {
return err
}

d.SetId(*repo.Name)
}
} else {
// Create without a repository template
repo, _, err := client.Repositories.Create(ctx, orgName, repoReq)
if err != nil {
return err
}
d.SetId(*repo.Name)
}
d.SetId(*repo.Name)

topics := repoReq.Topics
if len(topics) > 0 {
_, _, err = client.Repositories.ReplaceAllTopics(ctx, orgName, repoReq.GetName(), topics)
_, _, err = client.Repositories.ReplaceAllTopics(ctx, orgName, repoName, topics)
if err != nil {
return err
}
Expand Down Expand Up @@ -250,6 +308,18 @@ func resourceGithubRepositoryRead(d *schema.ResourceData, meta interface{}) erro
d.Set("http_clone_url", repo.CloneURL)
d.Set("archived", repo.Archived)
d.Set("topics", flattenStringList(repo.Topics))

if repo.TemplateRepository != nil {
d.Set("template", []interface{}{
map[string]interface{}{
"owner": repo.TemplateRepository.Owner.Login,
"repository": repo.TemplateRepository.Name,
},
})
} else {
d.Set("template", []interface{}{})
}

return nil
}

Expand Down
69 changes: 69 additions & 0 deletions github/resource_github_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,36 @@ func TestAccGithubRepository_autoInitForceNew(t *testing.T) {
})
}

func TestAccGithubRepository_createFromTemplate(t *testing.T) {
var repo github.Repository

rn := "github_repository.foo"
randString := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubRepositoryDestroy,
Steps: []resource.TestStep{
{
Config: testAccGithubRepositoryCreateFromTemplate(randString),
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubRepositoryExists(rn, &repo),
testAccCheckGithubRepositoryTemplateRepoAttribute(rn, &repo),
),
},
{
ResourceName: rn,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"auto_init",
},
},
},
})
}

func testAccCheckGithubRepositoryExists(n string, repo *github.Repository) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand All @@ -513,6 +543,17 @@ func testAccCheckGithubRepositoryExists(n string, repo *github.Repository) resou
}
}

func testAccCheckGithubRepositoryTemplateRepoAttribute(n string, repo *github.Repository) resource.TestCheckFunc {
return func(s *terraform.State) error {

if *repo.TemplateRepository.IsTemplate != true {
return fmt.Errorf("got repo %q; want %q", *repo.TemplateRepository, repo)
}

return nil
}
}

type testAccGithubRepositoryExpectedAttributes struct {
Name string
Description string
Expand Down Expand Up @@ -841,6 +882,34 @@ resource "github_repository" "foo" {
`, randString, randString)
}

func testAccGithubRepositoryCreateFromTemplate(randString string) string {
return fmt.Sprintf(`
resource "github_repository" "foo" {
name = "tf-acc-test-%s"
description = "Terraform acceptance tests %s"
homepage_url = "http://example.com/"

template {
# FIXME: Change this to something more suitable for CI runs
owner = "jcudit"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paultyng - If there is a better repository or organization to use, please let me know.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally try to avoid hard-coding organization or user names into the test suite, because that prevents anyone outside of the current team of maintainers from running these tests as they wouldn't have access to that organization or user.

We can leverage environment variables:
https://github.com/terraform-providers/terraform-provider-github/blob/2c03b951bb0aadc56d5f3e9a254f464d5ee018b8/github/provider_test.go#L58-L66

In this case I suppose we could leverage GITHUB_ORGANIZATION and introduce a new variable, say GITHUB_TEMPLATE_REPOSITORY?

repository = "terraform-template-module"
}

# So that acceptance tests can be run in a github organization
# with no billing
private = false

has_issues = true
has_wiki = true
allow_merge_commit = true
allow_squash_merge = false
allow_rebase_merge = false
has_downloads = true

}
`, randString, randString)
}

func testAccGithubRepositoryConfigTopics(randString string, topicList string) string {
return fmt.Sprintf(`
resource "github_repository" "foo" {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ require (
github.com/terraform-providers/terraform-provider-tls v1.2.0
golang.org/x/oauth2 v0.0.0-20190604054615-0f29369cfe45
)

go 1.13
radeksimko marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 14 additions & 1 deletion website/docs/r/repository.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ resource "github_repository" "example" {
description = "My awesome codebase"

private = true

template {
owner = "github"
repo = "terraform-module-template"
}
}
```

Expand Down Expand Up @@ -65,10 +70,18 @@ and after a correct reference has been created for the target branch inside the
initial repository creation and create the target branch inside of the repository prior to setting this attribute.

* `archived` - (Optional) Specifies if the repository should be archived. Defaults to `false`.
~> **NOTE** Currently, the API does not support unarchiving.

* `topics` - (Optional) The list of topics of the repository.

~> **NOTE** Currently, the API does not support unarchiving.
* `template` - (Optional) Use a template repository to create this resource. See [Template Repositories](#template-repositories) below for details.

### Template Repositories

`template` supports the following arguments:

* `owner`: The GitHub organization or user the template repository is owned by.
* `repository`: The name of the template repository.

## Attributes Reference

Expand Down