Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
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
9 changes: 7 additions & 2 deletions github/repository_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ import (
"strings"

"github.com/google/go-github/v82/github"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

// checkRepositoryBranchExists tests if a branch exists in a repository.
func checkRepositoryBranchExists(client *github.Client, owner, repo, branch string) error {
ctx := context.WithValue(context.Background(), ctxId, buildTwoPartID(repo, branch))
func checkRepositoryBranchExists(ctx context.Context, client *github.Client, owner, repo, branch string) error {
tflog.Debug(ctx, "Checking if branch exists", map[string]any{
"branch": branch,
"owner": owner,
"repo": repo,
})
_, _, err := client.Repositories.GetBranch(ctx, owner, repo, branch, 2)
if err != nil {
var ghErr *github.ErrorResponse
Expand Down
183 changes: 123 additions & 60 deletions github/resource_github_repository_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,28 @@ func resourceGithubRepositoryFile() *schema.Resource {
StateContext: resourceGithubRepositoryFileImport,
},

SchemaVersion: 1,
StateUpgraders: []schema.StateUpgrader{
{
Type: resourceGithubRepositoryFileV0().CoreConfigSchema().ImpliedType(),
Upgrade: resourceGithubRepositoryFileStateUpgradeV0,
Version: 0,
},
},

Description: "This resource allows you to create and manage files within a GitHub repository.",

Schema: map[string]*schema.Schema{
"repository": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The repository name",
},
"repository_id": {
Type: schema.TypeInt,
Computed: true,
Description: "The repository ID",
},
"file": {
Type: schema.TypeString,
Required: true,
Expand All @@ -47,6 +60,7 @@ func resourceGithubRepositoryFile() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
Description: "The branch name, defaults to the repository's default branch",
},
"ref": {
Expand Down Expand Up @@ -115,6 +129,7 @@ func resourceGithubRepositoryFile() *schema.Resource {
DiffSuppressFunc: autoBranchDiffSuppressFunc,
},
},
CustomizeDiff: diffRepository,
}
}

Expand Down Expand Up @@ -161,11 +176,18 @@ func resourceGithubRepositoryFileCreate(ctx context.Context, d *schema.ResourceD

checkOpt := github.RepositoryContentGetOptions{}

if branch, ok := d.GetOk("branch"); ok {
repoInfo, _, err := client.Repositories.Get(ctx, owner, repo)
Comment thread
deiga marked this conversation as resolved.
if err != nil {
return diag.FromErr(err)
}
var branch string

if branchFieldVal, ok := d.GetOk("branch"); ok {
branch = branchFieldVal.(string)
tflog.Debug(ctx, "Using explicitly set branch", map[string]any{
"branch": branch.(string),
"branch": branch,
})
if err := checkRepositoryBranchExists(client, owner, repo, branch.(string)); err != nil {
if err := checkRepositoryBranchExists(ctx, client, owner, repo, branch); err != nil {
if d.Get("autocreate_branch").(bool) {
if err := resourceGithubRepositoryFileCreateBranch(ctx, d, meta); err != nil {
return diag.FromErr(err)
Expand All @@ -174,7 +196,13 @@ func resourceGithubRepositoryFileCreate(ctx context.Context, d *schema.ResourceD
return diag.FromErr(err)
}
}
checkOpt.Ref = branch.(string)
checkOpt.Ref = branch
Comment thread
deiga marked this conversation as resolved.
Outdated
} else {
branch = repoInfo.GetDefaultBranch()
}

if err := d.Set("repository_id", int(repoInfo.GetID())); err != nil {
return diag.FromErr(err)
}
Comment thread
deiga marked this conversation as resolved.
Outdated

opts := resourceGithubRepositoryFileOptions(d)
Expand Down Expand Up @@ -215,7 +243,18 @@ func resourceGithubRepositoryFileCreate(ctx context.Context, d *schema.ResourceD
return diag.FromErr(err)
}

d.SetId(fmt.Sprintf("%s/%s", repo, file))
if err := d.Set("branch", branch); err != nil {
return diag.FromErr(err)
}
Comment thread
deiga marked this conversation as resolved.
Outdated

newResourceID, err := buildID(repo, file, branch)
if err != nil {
return diag.FromErr(err)
}
tflog.Debug(ctx, "Setting ID", map[string]any{
"id": newResourceID,
})
d.SetId(newResourceID)
if err = d.Set("commit_sha", create.GetSHA()); err != nil {
return diag.FromErr(err)
}
Expand All @@ -227,30 +266,34 @@ func resourceGithubRepositoryFileRead(ctx context.Context, d *schema.ResourceDat
client := meta.(*Owner).v3client
owner := meta.(*Owner).name

repo, file := splitRepoFilePath(d.Id())
ctx = tflog.SetField(ctx, "repository", repo)
repoName, ok := d.Get("repository").(string)
if !ok {
return diag.FromErr(fmt.Errorf("repository not found or is not a string"))
}
file, ok := d.Get("file").(string)
if !ok {
return diag.FromErr(fmt.Errorf("file not found or is not a string"))
}
Comment thread
deiga marked this conversation as resolved.
Outdated

ctx = tflog.SetField(ctx, "repository", repoName)
ctx = tflog.SetField(ctx, "file", file)
ctx = tflog.SetField(ctx, "owner", owner)

opts := &github.RepositoryContentGetOptions{}

if branch, ok := d.GetOk("branch"); ok {
tflog.Debug(ctx, "Using explicitly set branch", map[string]any{
"branch": branch.(string),
})
if err := checkRepositoryBranchExists(client, owner, repo, branch.(string)); err != nil {
if d.Get("autocreate_branch").(bool) {
branch = d.Get("autocreate_branch_source_branch").(string)
} else {
tflog.Info(ctx, "Removing repository path from state because the branch no longer exists in GitHub")
d.SetId("")
return nil
}
branch := d.Get("branch").(string)
if err := checkRepositoryBranchExists(ctx, client, owner, repoName, branch); err != nil {
Comment thread
deiga marked this conversation as resolved.
Outdated
if d.Get("autocreate_branch").(bool) {
branch = d.Get("autocreate_branch_source_branch").(string)
Comment thread
deiga marked this conversation as resolved.
Outdated
} else {
tflog.Info(ctx, "Removing repository path from state because the branch no longer exists in GitHub")
d.SetId("")
return nil
}
opts.Ref = branch.(string)
}
opts.Ref = branch

fc, _, _, err := client.Repositories.GetContents(ctx, owner, repo, file, opts)
fc, _, _, err := client.Repositories.GetContents(ctx, owner, repoName, file, opts)
if err != nil {
var errorResponse *github.ErrorResponse
if errors.As(err, &errorResponse) && errorResponse.Response.StatusCode == http.StatusTooManyRequests {
Comment thread
deiga marked this conversation as resolved.
Outdated
Expand All @@ -271,12 +314,7 @@ func resourceGithubRepositoryFileRead(ctx context.Context, d *schema.ResourceDat
if err = d.Set("content", content); err != nil {
return diag.FromErr(err)
}
if err = d.Set("repository", repo); err != nil {
return diag.FromErr(err)
}
if err = d.Set("file", file); err != nil {
return diag.FromErr(err)
}

if err = d.Set("sha", fc.GetSHA()); err != nil {
return diag.FromErr(err)
}
Expand All @@ -301,10 +339,10 @@ func resourceGithubRepositoryFileRead(ctx context.Context, d *schema.ResourceDat
tflog.Debug(ctx, "Using known commit SHA", map[string]any{
"commit_sha": sha.(string),
})
commit, _, err = client.Repositories.GetCommit(ctx, owner, repo, sha.(string), nil)
commit, _, err = client.Repositories.GetCommit(ctx, owner, repoName, sha.(string), nil)
} else {
tflog.Debug(ctx, "Commit SHA unknown for file, looking for commit")
commit, err = getFileCommit(ctx, client, owner, repo, file, ref)
commit, err = getFileCommit(ctx, client, owner, repoName, file, ref)
}
if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -349,18 +387,15 @@ func resourceGithubRepositoryFileUpdate(ctx context.Context, d *schema.ResourceD
ctx = tflog.SetField(ctx, "file", file)
ctx = tflog.SetField(ctx, "owner", owner)

if branch, ok := d.GetOk("branch"); ok {
tflog.Debug(ctx, "Using explicitly set branch", map[string]any{
"branch": branch.(string),
})
if err := checkRepositoryBranchExists(client, owner, repo, branch.(string)); err != nil {
if d.Get("autocreate_branch").(bool) {
if err := resourceGithubRepositoryFileCreateBranch(ctx, d, meta); err != nil {
return diag.FromErr(err)
}
} else {
branch := d.Get("branch").(string)
Comment thread
deiga marked this conversation as resolved.
Outdated

if err := checkRepositoryBranchExists(ctx, client, owner, repo, branch); err != nil {
if d.Get("autocreate_branch").(bool) {
if err := resourceGithubRepositoryFileCreateBranch(ctx, d, meta); err != nil {
return diag.FromErr(err)
}
} else {
return diag.FromErr(err)
}
}

Expand Down Expand Up @@ -395,22 +430,18 @@ func resourceGithubRepositoryFileDelete(ctx context.Context, d *schema.ResourceD
opts.Message = github.Ptr(fmt.Sprintf("Delete %s", file))
}

if b, ok := d.GetOk("branch"); ok {
tflog.Debug(ctx, "Using explicitly set branch", map[string]any{
"branch": b.(string),
})
if err := checkRepositoryBranchExists(client, owner, repo, b.(string)); err != nil {
if d.Get("autocreate_branch").(bool) {
if err := resourceGithubRepositoryFileCreateBranch(ctx, d, meta); err != nil {
return diag.FromErr(err)
}
} else {
branch := d.Get("branch").(string)

if err := checkRepositoryBranchExists(ctx, client, owner, repo, branch); err != nil {
if d.Get("autocreate_branch").(bool) {
if err := resourceGithubRepositoryFileCreateBranch(ctx, d, meta); err != nil {
return diag.FromErr(err)
}
} else {
return diag.FromErr(err)
}
branch := b.(string)
opts.Branch = github.Ptr(branch)
}
opts.Branch = github.Ptr(branch)

_, _, err := client.Repositories.DeleteFile(ctx, owner, repo, file, opts)
return diag.FromErr(handleArchivedRepoDelete(err, "repository file", file, owner, repo))
Expand All @@ -427,28 +458,60 @@ func autoBranchDiffSuppressFunc(k, _, _ string, d *schema.ResourceData) bool {
}

func resourceGithubRepositoryFileImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
repoFilePath, branch, err := parseID2(d.Id())
repo, filePath, branch, err := parseID3(d.Id())
if err != nil {
return nil, fmt.Errorf("invalid ID specified. Supplied ID must be written as <repository>/<file path>:<branch>. %w", err)
return nil, fmt.Errorf("invalid ID specified. Supplied ID must be written as <repository>:<file path>: (when branch is default) or <repository>:<file path>:<branch>. %w", err)
}

client := meta.(*Owner).v3client
owner := meta.(*Owner).name
repo, file := splitRepoFilePath(repoFilePath)

opts := &github.RepositoryContentGetOptions{Ref: branch}
if err := d.Set("branch", branch); err != nil {
opts := &github.RepositoryContentGetOptions{}

repoInfo, _, err := client.Repositories.Get(ctx, owner, repo)
Comment thread
deiga marked this conversation as resolved.
Comment thread
deiga marked this conversation as resolved.
if err != nil {
return nil, err
}
if branch != "" {
opts.Ref = branch
if err := d.Set("branch", branch); err != nil {
return nil, err
}
} else {

defaultBranch := repoInfo.GetDefaultBranch()
branch = defaultBranch
if err := d.Set("branch", branch); err != nil {
return nil, err
}
}
Comment thread
deiga marked this conversation as resolved.
Outdated
if err := d.Set("repository_id", int(repoInfo.GetID())); err != nil {
return nil, err
}
fc, _, _, err := client.Repositories.GetContents(ctx, owner, repo, file, opts)

fc, _, _, err := client.Repositories.GetContents(ctx, owner, repo, filePath, opts)
if err != nil {
return nil, err
}
if fc == nil {
return nil, fmt.Errorf("file %s is not a file in repository %s/%s or repository is not readable", file, owner, repo)
return nil, fmt.Errorf("filePath %s is not a file in repository %s/%s or repository is not readable", filePath, owner, repo)
}

if err := d.Set("repository", repo); err != nil {
return nil, err
}
if err := d.Set("file", filePath); err != nil {
return nil, err
}

d.SetId(fmt.Sprintf("%s/%s", repo, file))
newResourceID, err := buildID(repo, filePath, branch)
if err != nil {
return nil, err
}
tflog.Debug(ctx, "Setting ID", map[string]any{
"id": newResourceID,
})
d.SetId(newResourceID)
if err = d.Set("overwrite_on_create", false); err != nil {
return nil, err
}
Expand Down
Loading