From 1ed35073d7951062127f8bcf7ac0d7b03594c653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Torres=20Cogollo?= Date: Fri, 25 Apr 2025 15:12:41 +0200 Subject: [PATCH 1/3] fix(github_repository_file): Commit detail changes should not do empty commits --- examples/repository_file/README.md | 24 +++++++++++++ examples/repository_file/main.tf | 11 ++++++ examples/repository_file/outputs.tf | 0 examples/repository_file/providers.tf | 12 +++++++ examples/repository_file/variables.tf | 43 +++++++++++++++++++++++ github/resource_github_repository_file.go | 28 ++++++++++----- 6 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 examples/repository_file/README.md create mode 100644 examples/repository_file/main.tf create mode 100644 examples/repository_file/outputs.tf create mode 100644 examples/repository_file/providers.tf create mode 100644 examples/repository_file/variables.tf diff --git a/examples/repository_file/README.md b/examples/repository_file/README.md new file mode 100644 index 0000000000..4b6f8b588e --- /dev/null +++ b/examples/repository_file/README.md @@ -0,0 +1,24 @@ +# Repository File + +This provides a template for managing [repository files](https://docs.github.com/en/repositories/working-with-files/managing-files). + +This example will also create or update a file in the specified `repository`. See https://www.terraform.io/docs/providers/github/index.html for details on configuring [`providers.tf`](./providers.tf) accordingly. + +Alternatively, you may use variables passed via the command line or `auto.tfvars`: + +```tfvars +organization = "" +github_token = "" + +repository = "" +file = "" +content = "" +branch = "" +commit_author = "" +commit_message = "" +commit_email = "" +``` + +```console +terraform apply +``` diff --git a/examples/repository_file/main.tf b/examples/repository_file/main.tf new file mode 100644 index 0000000000..bab860326a --- /dev/null +++ b/examples/repository_file/main.tf @@ -0,0 +1,11 @@ +resource "github_repository_file" "this" { + repository = var.repository + file = var.file + content = var.content + + branch = var.branch + + commit_author = var.commit_author + commit_message = var.commit_message + commit_email = var.commit_email +} diff --git a/examples/repository_file/outputs.tf b/examples/repository_file/outputs.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/repository_file/providers.tf b/examples/repository_file/providers.tf new file mode 100644 index 0000000000..43f79f6456 --- /dev/null +++ b/examples/repository_file/providers.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + } + } +} + +provider "github" { + owner = var.organization + token = var.github_token +} diff --git a/examples/repository_file/variables.tf b/examples/repository_file/variables.tf new file mode 100644 index 0000000000..cd5ed6272e --- /dev/null +++ b/examples/repository_file/variables.tf @@ -0,0 +1,43 @@ +variable "organization" { + description = "GitHub organization used to configure the provider" + type = string +} + +variable "github_token" { + description = "GitHub access token used to configure the provider" + type = string +} + +variable "repository" { + description = "The name of the repository" + type = string +} + +variable "file" { + description = "The name of the file to create" + type = string +} +variable "content" { + description = "The content of the file to create" + type = string +} +variable "branch" { + description = "The branch to create the file in" + type = string + default = "main" +} +variable "commit_author" { + description = "The name of the author of the commit" + type = string + default = "" +} +variable "commit_message" { + description = "The message of the commit" + type = string + default = "" +} +variable "commit_email" { + description = "The email of the author of the commit" + type = string + default = "" +} diff --git a/github/resource_github_repository_file.go b/github/resource_github_repository_file.go index c84a8a1953..e6ef14edeb 100644 --- a/github/resource_github_repository_file.go +++ b/github/resource_github_repository_file.go @@ -3,13 +3,13 @@ package github import ( "context" "errors" + "fmt" "log" "net/http" "net/url" + "slices" "strings" - "fmt" - "github.com/google/go-github/v66/github" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -437,20 +437,30 @@ func resourceGithubRepositoryFileUpdate(d *schema.ResourceData, meta interface{} opts.Message = &m } - create, _, err := client.Repositories.CreateFile(ctx, owner, repo, file, opts) - if err != nil { - return err + schema := resourceGithubRepositoryFile() + allSchemaKeys := make([]string, 0, len(schema.Schema)) + for k := range schema.Schema { + allSchemaKeys = append(allSchemaKeys, k) } + allSchemaKeysButCommitDetails := slices.DeleteFunc(allSchemaKeys, func(v string) bool { + return slices.Contains([]string{"commit_sha", "commit_author", "commit_email", "commit_message"}, v) + }) - if err = d.Set("commit_sha", create.GetSHA()); err != nil { - return err + if d.HasChanges(allSchemaKeysButCommitDetails...) { + create, _, err := client.Repositories.CreateFile(ctx, owner, repo, file, opts) + if err != nil { + return err + } + if err = d.Set("commit_sha", create.GetSHA()); err != nil { + return err + } + } else { + log.Printf("[DEBUG] No changes to commit, skipping commit") } - return resourceGithubRepositoryFileRead(d, meta) } func resourceGithubRepositoryFileDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*Owner).v3client owner := meta.(*Owner).name ctx := context.Background() From 1c2e36174f1f7e77252b6e1d5f8276e740040b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Torres=20Cogollo?= Date: Tue, 29 Apr 2025 17:15:21 +0200 Subject: [PATCH 2/3] Fix committer details lifecycle issues --- github/resource_github_repository_file.go | 33 +++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/github/resource_github_repository_file.go b/github/resource_github_repository_file.go index e6ef14edeb..a9ee9440bb 100644 --- a/github/resource_github_repository_file.go +++ b/github/resource_github_repository_file.go @@ -367,23 +367,34 @@ func resourceGithubRepositoryFileRead(d *schema.ResourceData, meta interface{}) return err } - commit_author := commit.Commit.GetCommitter().GetName() - commit_email := commit.Commit.GetCommitter().GetEmail() - - _, hasCommitAuthor := d.GetOk("commit_author") - _, hasCommitEmail := d.GetOk("commit_email") + if commit_author_st, hasCommitAuthor := d.GetOk("commit_author"); hasCommitAuthor { + if err = d.Set("commit_author", commit_author_st.(string)); err != nil { + return err + } + } else { + if err = d.Set("commit_author", nil); err != nil { + return err + } + } - //read from state if author+email is set explicitly, and if it was not github signing it for you previously - if commit_author != "GitHub" && commit_email != "noreply@github.com" && hasCommitAuthor && hasCommitEmail { - if err = d.Set("commit_author", commit_author); err != nil { + if commit_email_st, hasCommitEmail := d.GetOk("commit_email"); hasCommitEmail { + if err = d.Set("commit_email", commit_email_st.(string)); err != nil { return err } - if err = d.Set("commit_email", commit_email); err != nil { + } else { + if err = d.Set("commit_email", nil); err != nil { return err } } - if err = d.Set("commit_message", commit.GetCommit().GetMessage()); err != nil { - return err + + if commit_message_st, hasCommitMessage := d.GetOk("commit_message"); hasCommitMessage { + if err = d.Set("commit_message", commit_message_st.(string)); err != nil { + return err + } + } else { + if err = d.Set("commit_message", nil); err != nil { + return err + } } return nil From c7594f72936c7f1ed3a3622f55ba4863f695f5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Torres=20Cogollo?= Date: Thu, 11 Dec 2025 12:27:02 +0100 Subject: [PATCH 3/3] Fix nil pointer dereference when commit_message is not set The Update and Delete functions were dereferencing opts.Message without checking for nil, which could cause a panic when users don't specify a commit_message in their configuration. Added nil checks before dereferencing to use default messages when no custom message is provided. --- github/resource_github_repository_file.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github/resource_github_repository_file.go b/github/resource_github_repository_file.go index 9bf72f5ed1..33e51b665e 100644 --- a/github/resource_github_repository_file.go +++ b/github/resource_github_repository_file.go @@ -440,7 +440,7 @@ func resourceGithubRepositoryFileUpdate(d *schema.ResourceData, meta any) error return err } - if *opts.Message == fmt.Sprintf("Add %s", file) { + if opts.Message == nil || *opts.Message == fmt.Sprintf("Add %s", file) { m := fmt.Sprintf("Update %s", file) opts.Message = &m } @@ -483,7 +483,7 @@ func resourceGithubRepositoryFileDelete(d *schema.ResourceData, meta any) error return err } - if *opts.Message == fmt.Sprintf("Add %s", file) { + if opts.Message == nil || *opts.Message == fmt.Sprintf("Add %s", file) { m := fmt.Sprintf("Delete %s", file) opts.Message = &m }