From 1590fd90c636863577570d18f80bdce60b3d56bf Mon Sep 17 00:00:00 2001 From: ilaif Date: Wed, 17 Apr 2024 01:03:45 +0300 Subject: [PATCH] deprecation: retire .ignore_pull_request_template variable feat: add .pull_request_template_path to customize the template path enhancement: support finding the pr template and .gh-prx.yaml paths from nested repo directories --- README.md | 5 ++--- pkg/cmd/create.go | 23 +++++++++++------------ pkg/config/repository_config.go | 24 ++++++++++++++++-------- pkg/utils/file.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 99212ea..cb86425 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,6 @@ pr: issue: provider: github # The provider to use for fetching issue details (supported: github,jira,linear) types: ["fix", "feat", "chore", "docs", "refactor", "test", "style", "build", "ci", "perf", "revert"] # The issue types to prompt the user when creating a new branch - pull_request_template_path: "./pull_request_template.md" # The pull request template file to use when creating a new PR. Relative to the .gh-prx.yaml file location. - ignore_pull_request_template: false # If true, the pull request template in the repository will be ignored. checkout_new: jira: project: "" # The Jira project key to use when creating a new branch @@ -91,11 +89,12 @@ checkout_new: github: issue_list_flags: ["--state", open", "--assignee", "@me"] # The flags to use when fetching issues from GitHub # linear: # Due to Linear's GraphQL API, the issue list is not configurable. The default is: `assignedIssues(orderBy: updatedAt, filter: { state: { type: { neq: \"completed\" } } })` +pull_request_template_path: "./pull_request_template.md" # The pull request template file to use when creating a new PR. Relative to the repository root. ``` ### PR Description (Body) -The PR description is based on the repo's `.github/pull_request_template.md`. If this file does not exist, a default template is used: +The PR description is based on the `pull_request_template_path` variable which defaults to the repo's `.github/pull_request_template.md`. If this file does not exist, a default template is used: ```markdown {{with .Issue}}Closes #{{.}}. diff --git a/pkg/cmd/create.go b/pkg/cmd/create.go index 2a23356..4f72b0b 100644 --- a/pkg/cmd/create.go +++ b/pkg/cmd/create.go @@ -148,16 +148,15 @@ func create(ctx context.Context, opts *CreateOpts) error { } if cfg.PullRequestTemplatePath != "" { - prTemplateBytes, err := utils.ReadFile(cfg.PullRequestTemplatePath) + prTemplatePath, err := utils.FindRelativePathInRepo(cfg.PullRequestTemplatePath) + if err != nil { + return errors.Wrap(err, "Failed to find pull request template path") + } + prTemplateBytes, err := utils.ReadFile(prTemplatePath) if err != nil { return errors.Wrap(err, "Failed to read pull request template") } cfg.PR.Body = string(prTemplateBytes) - } else if cfg.IgnorePullRequestTemplate == nil || !*cfg.IgnorePullRequestTemplate { - prTemplateBytes, err := utils.ReadFile(".github/pull_request_template.md") - if err == nil { - cfg.PR.Body = string(prTemplateBytes) - } } out, err := utils.Exec("git", "log", "--pretty=format:%s", "--no-merges", b.Original, "^"+baseBranch) @@ -197,18 +196,18 @@ func create(ctx context.Context, opts *CreateOpts) error { log.Debug(fmt.Sprintf("Pull request body:\n\n%s", pr.Body)) log.Debug(fmt.Sprintf("Pull request labels: %v", pr.Labels)) - if len(pr.Labels) > 0 { - if err := createLabels(pr.Labels); err != nil { - return err - } - } - if opts.DryRun { log.Info("Dry run enabled, skipping pull request creation") return nil } + if len(pr.Labels) > 0 { + if err := createLabels(pr.Labels); err != nil { + return err + } + } + s := utils.StartSpinner("Creating pull request...", "Created pull request") args := []string{"pr", "create", "--title", pr.Title, "--body", pr.Body, "--base", baseBranch} args = append(args, generatePrCreateArgsFromOpts(opts, pr.Labels)...) diff --git a/pkg/config/repository_config.go b/pkg/config/repository_config.go index d993481..1d117d6 100644 --- a/pkg/config/repository_config.go +++ b/pkg/config/repository_config.go @@ -57,12 +57,11 @@ var ( ) type RepositoryConfig struct { - Branch BranchConfig `yaml:"branch"` - PR PullRequestConfig `yaml:"pr"` - Issue IssueConfig `yaml:"issue"` - PullRequestTemplatePath string `yaml:"pull_request_template_path"` - IgnorePullRequestTemplate *bool `yaml:"ignore_pull_request_template"` - CheckoutNew CheckoutNewConfig `yaml:"checkout_new"` + Branch BranchConfig `yaml:"branch"` + PR PullRequestConfig `yaml:"pr"` + Issue IssueConfig `yaml:"issue"` + CheckoutNew CheckoutNewConfig `yaml:"checkout_new"` + PullRequestTemplatePath string `yaml:"pull_request_template_path"` } func (c *RepositoryConfig) SetDefaults() { @@ -70,6 +69,10 @@ func (c *RepositoryConfig) SetDefaults() { c.PR.SetDefaults() c.Issue.SetDefaults() c.CheckoutNew.SetDefaults() + + if c.PullRequestTemplatePath == "" { + c.PullRequestTemplatePath = ".github/pull_request_template.md" + } } func (c *RepositoryConfig) Validate() error { @@ -251,12 +254,17 @@ func (c *CheckoutNewGitHubConfig) SetDefaults() { func LoadRepositoryConfig(globalRepoConfig *RepositoryConfig) (*RepositoryConfig, error) { cfg := &RepositoryConfig{} - if err := utils.ReadYaml(DefaultConfigFilepath, cfg); err != nil { + + if actualConfigFilepath, err := utils.FindRelativePathInRepo(DefaultConfigFilepath); err != nil { if !errors.Is(err, os.ErrNotExist) { return nil, errors.Wrap(err, "Failed to load config") } - log.Infof("No config file found at '%s', using defaults", DefaultConfigFilepath) + } else { + log.Debug(fmt.Sprintf("Loading repository config from '%s'", actualConfigFilepath)) + if err := utils.ReadYaml(actualConfigFilepath, cfg); err != nil { + return nil, errors.Wrap(err, "Failed to load config") + } } if globalRepoConfig != nil { diff --git a/pkg/utils/file.go b/pkg/utils/file.go index fbb95b7..9f6f563 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -56,3 +56,34 @@ func WriteYaml(filename string, data interface{}) error { return nil } + +// FindRelativePathInRepo traverses up from the current directory recursively until a relative path is found. +// It returns the first relative path found, or os.ErrNotExists if no relative path is found. +func FindRelativePathInRepo(path string) (string, error) { + // Get the current working directory + currentDir, err := os.Getwd() + if err != nil { + return "", errors.Wrap(err, "Failed to get current working directory") + } + + // Start traversing up from the current directory + for { + // Check if the current directory contains a relative path + if _, err := os.Stat(filepath.Join(currentDir, path)); err == nil { + return filepath.Join(currentDir, path), nil + } + + // Move up to the parent directory + parentDir := filepath.Dir(currentDir) + + // Check if we have reached the git root directory + if _, err := os.Stat(filepath.Join(currentDir, ".git")); err == nil { + break // Break the loop if we have reached the git root directory + } + + // Update the current directory to the parent directory + currentDir = parentDir + } + + return "", os.ErrNotExist // Return an error if no relative path is found +}