diff --git a/github/provider.go b/github/provider.go index e2c117501e..e435996475 100644 --- a/github/provider.go +++ b/github/provider.go @@ -98,6 +98,7 @@ func Provider() terraform.ResourceProvider { "github_organization_webhook": resourceGithubOrganizationWebhook(), "github_project_card": resourceGithubProjectCard(), "github_project_column": resourceGithubProjectColumn(), + "github_repository_autolink_reference": resourceGithubRepositoryAutolinkReference(), "github_repository_collaborator": resourceGithubRepositoryCollaborator(), "github_repository_deploy_key": resourceGithubRepositoryDeployKey(), "github_repository_environment": resourceGithubRepositoryEnvironment(), diff --git a/github/resource_github_repository_autolink_reference.go b/github/resource_github_repository_autolink_reference.go new file mode 100644 index 0000000000..2352a73f6d --- /dev/null +++ b/github/resource_github_repository_autolink_reference.go @@ -0,0 +1,128 @@ +package github + +import ( + "context" + "fmt" + "log" + "regexp" + "strconv" + "strings" + + "github.com/google/go-github/v39/github" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceGithubRepositoryAutolinkReference() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubRepositoryAutolinkReferenceCreate, + Read: resourceGithubRepositoryAutolinkReferenceRead, + Delete: resourceGithubRepositoryAutolinkReferenceDelete, + + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), "/") + if len(parts) != 2 { + return nil, fmt.Errorf("Invalid ID specified. Supplied ID must be written as /") + } + d.Set("repository", parts[0]) + d.SetId(parts[1]) + return []*schema.ResourceData{d}, nil + }, + }, + + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The repository name", + }, + "key_prefix": { + Type: schema.TypeString, + Required: true, + Description: "This prefix appended by a number will generate a link any time it is found in an issue, pull request, or commit", + }, + "target_url_template": { + Type: schema.TypeString, + Required: true, + Description: "The template of the target URL used for the links; must be a valid URL and contain `` for the reference number", + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^http[s]?:\/\/[a-z0-9-.]*\/.*?.*?$`), "must be a valid URL and contain token"), + }, + "etag": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceGithubRepositoryAutolinkReferenceCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + + owner := meta.(*Owner).name + repoName := d.Get("repository").(string) + keyPrefix := d.Get("key_prefix").(string) + targetURLTemplate := d.Get("target_url_template").(string) + ctx := context.Background() + + opts := &github.AutolinkOptions{ + KeyPrefix: &keyPrefix, + URLTemplate: &targetURLTemplate, + } + + log.Printf("[DEBUG] Creating repository autolink reference: %s -> %s (%s/%s)", keyPrefix, targetURLTemplate, owner, repoName) + autolinkRef, _, err := client.Repositories.AddAutolink(ctx, owner, repoName, opts) + if err != nil { + return err + } + d.SetId(strconv.FormatInt(autolinkRef.GetID(), 10)) + + return resourceGithubRepositoryAutolinkReferenceRead(d, meta) +} + +func resourceGithubRepositoryAutolinkReferenceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + + owner := meta.(*Owner).name + repoName := d.Get("repository").(string) + autolinkRefID, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return unconvertibleIdErr(d.Id(), err) + } + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + if !d.IsNewResource() { + ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string)) + } + + log.Printf("[DEBUG] Reading repository autolink reference: %s (%s/%s)", d.Id(), owner, repoName) + autolinkRef, _, err := client.Repositories.GetAutolink(ctx, owner, repoName, autolinkRefID) + if err != nil { + return err + } + + // Set resource fields + d.SetId(strconv.FormatInt(autolinkRef.GetID(), 10)) + d.Set("repository", repoName) + d.Set("key_prefix", autolinkRef.KeyPrefix) + d.Set("target_url_template", autolinkRef.URLTemplate) + + return nil +} + +func resourceGithubRepositoryAutolinkReferenceDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + + owner := meta.(*Owner).name + repoName := d.Get("repository").(string) + autolinkRefID, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return unconvertibleIdErr(d.Id(), err) + } + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + log.Printf("[DEBUG] Deleting repository autolink reference: %s (%s/%s)", d.Id(), owner, repoName) + _, err = client.Repositories.DeleteAutolink(ctx, owner, repoName, autolinkRefID) + return err +} diff --git a/github/resource_github_repository_autolink_reference_test.go b/github/resource_github_repository_autolink_reference_test.go new file mode 100644 index 0000000000..4d7cadd22a --- /dev/null +++ b/github/resource_github_repository_autolink_reference_test.go @@ -0,0 +1,156 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccGithubRepositoryAutolinkReference(t *testing.T) { + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + t.Run("creates repository autolink reference without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "oof" { + name = "oof-%s" + description = "Test autolink creation" + } + + resource "github_repository_autolink_reference" "autolink" { + repository = github_repository.oof.name + + key_prefix = "OOF-" + target_url_template = "https://awesome.com/find/OOF-" + } + `, randomID) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_repository_autolink_reference.autolink", "key_prefix", "OOF-", + ), + resource.TestCheckResourceAttr( + "github_repository_autolink_reference.autolink", "target_url_template", "https://awesome.com/find/OOF-", + ), + ) + + 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) + }) + + }) + + t.Run("imports repository autolink reference without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "oof" { + name = "oof-%s" + description = "Test autolink creation" + } + + resource "github_repository_autolink_reference" "autolink" { + repository = github_repository.oof.name + + key_prefix = "OOF-" + target_url_template = "https://awesome.com/find/OOF-" + } + `, randomID) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: "github_repository_autolink_reference.autolink", + ImportState: true, + ImportStateVerify: true, + ImportStateIdPrefix: fmt.Sprintf("oof-%s/", randomID), + }, + }, + }) + } + + 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) + }) + + }) + + t.Run("deletes repository autolink reference without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_repository" "oof" { + name = "oof-%s" + description = "Test autolink creation" + } + + resource "github_repository_autolink_reference" "autolink" { + repository = github_repository.oof.name + + key_prefix = "OOF-" + target_url_template = "https://awesome.com/find/OOF-" + } + `, randomID) + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Destroy: true, + }, + }, + }) + } + + 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) + }) + }) +} diff --git a/website/docs/r/repository_autolink_reference.html.markdown b/website/docs/r/repository_autolink_reference.html.markdown new file mode 100644 index 0000000000..8f9ed6870a --- /dev/null +++ b/website/docs/r/repository_autolink_reference.html.markdown @@ -0,0 +1,53 @@ +--- +layout: "github" +page_title: "GitHub: github_repository_autolink_reference" +description: |- + Creates and manages autolink references for a single repository +--- + +# github_repository_autolink_reference + +This resource allows you to create and manage an autolink reference for a single repository. + +## Example Usage + +```hcl +resource "github_repository" "repo" { + name = "oof" + description = "GitHub repo managed by Terraform" + + private = false +} + +resource "github_repository_autolink_reference" "auto" { + repository = github_repository.repo.name + + key_prefix = "TICKET-" + + target_url_template = "https://hello.there/TICKET?query=" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `repository` - (Required) The repository of the autolink reference. + +* `key_prefix` - (Required) This prefix appended by a number will generate a link any time it is found in an issue, pull request, or commit. + +* `target_url_template` - (Required) The template of the target URL used for the links; must be a valid URL and contain `` for the reference number + +## Attributes Reference + +The following additional attributes are exported: + +* `etag` - An etag representing the autolink reference object. + +## Import + +Autolink references can be imported using the `name` of the repository, combined with the `id` of the autolink reference and a `/` character for separating components, e.g. + +```sh +terraform import github_repository_autolink_reference.auto oof/123 +``` diff --git a/website/github.erb b/website/github.erb index c8ab0aeccc..f23f3d2828 100644 --- a/website/github.erb +++ b/website/github.erb @@ -115,6 +115,9 @@
  • github_repository
  • +
  • + github_repository_autolink_reference +
  • github_repository_collaborator