diff --git a/gitcmds/gitcmds.go b/gitcmds/gitcmds.go index 19cfac5..58a6cfc 100644 --- a/gitcmds/gitcmds.go +++ b/gitcmds/gitcmds.go @@ -821,7 +821,7 @@ func GetIssueDescription(notes []string) (string, error) { description, err = GetGitHubIssueDescription(notesObj.GithubIssueURL) case len(notesObj.JiraTicketURL) > 0: var jiraTicketID string - description, jiraTicketID, err = jira.GetJiraIssueName(notesObj.JiraTicketURL, "") + description, jiraTicketID, err = jira.GetJiraIssueTitle(notesObj.JiraTicketURL, "") if err != nil { return "", err } diff --git a/gitcmds/github.go b/gitcmds/github.go index 3111962..f42acd8 100644 --- a/gitcmds/github.go +++ b/gitcmds/github.go @@ -9,19 +9,16 @@ import ( "encoding/json" "errors" "fmt" - "regexp" - "strconv" "strings" "github.com/untillpro/goutils/exec" "github.com/untillpro/goutils/logger" - notesPkg "github.com/untillpro/qs/internal/notes" "github.com/untillpro/qs/utils" ) // LinkBranchToGithubIssue links an existing remote branch to a GitHub issue and prepares notes. // The branch must already exist on the remote before calling this function. -func LinkBranchToGithubIssue(wd, parentRepo, githubIssueURL string, issueNumber int, branchName string, args ...string) (notes []string, err error) { +func LinkBranchToGithubIssue(wd, parentRepo, githubIssueURL, issueNumber, branchName string, args ...string) (notes []string, err error) { repo, org, err := GetRepoAndOrgName(wd) if err != nil { return nil, fmt.Errorf("GetRepoAndOrgName failed: %w", err) @@ -31,7 +28,6 @@ func LinkBranchToGithubIssue(wd, parentRepo, githubIssueURL string, issueNumber return nil, errors.New(repoNotFound) } - strIssueNum := strconv.Itoa(issueNumber) myrepo := org + slash + repo if len(args) > 0 { @@ -63,7 +59,7 @@ func LinkBranchToGithubIssue(wd, parentRepo, githubIssueURL string, issueNumber } stdout, stderr, err = new(exec.PipedExec). - Command("gh", "issue", "develop", strIssueNum, "--branch-repo="+myrepo, "--repo="+parentRepo, "--name="+branchName, "--base="+mainBranch). + Command("gh", "issue", "develop", issueNumber, "--branch-repo="+myrepo, "--repo="+parentRepo, "--name="+branchName, "--base="+mainBranch). WorkingDir(wd). RunToStrings() if err != nil { @@ -100,64 +96,6 @@ func GetGithubIssueRepoFromURL(url string) (repoName string) { return } -func BuildDevBranchName(issueURL string) (string, []string, error) { - parts := strings.Split(issueURL, slash) - if len(parts) < 2 { - return "", nil, fmt.Errorf("invalid issue URL format: %s", issueURL) - } - issueNumber := parts[len(parts)-1] - - repoURL := strings.Split(issueURL, "/issues/")[0] - urlParts := strings.Split(repoURL, slash) - if len(urlParts) < 5 { //nolint:revive - return "", nil, fmt.Errorf("invalid GitHub URL format: %s", repoURL) - } - owner := urlParts[3] //nolint:revive - repo := urlParts[4] //nolint:revive - - stdout, stderr, err := new(exec.PipedExec). - Command("gh", "issue", "view", issueNumber, "--repo", fmt.Sprintf("%s/%s", owner, repo), "--json", "title"). - RunToStrings() - if err != nil { - logger.Verbose(stderr) - if len(stderr) > 0 { - return "", nil, errors.New(stderr) - } - return "", nil, fmt.Errorf("failed to get issue title: %w", err) - } - logger.Verbose(stdout) - - var issueData struct { - Title string `json:"title"` - } - if err := json.Unmarshal([]byte(stdout), &issueData); err != nil { - return "", nil, fmt.Errorf("failed to parse issue data: %w", err) - } - - kebabTitle := strings.ToLower(issueData.Title) - kebabTitle = regexp.MustCompile(`[^a-z0-9]+`).ReplaceAllString(kebabTitle, "-") - kebabTitle = strings.Trim(kebabTitle, "-") - - branchName := fmt.Sprintf("%s-%s", issueNumber, kebabTitle) - if len(branchName) > maximumBranchNameLength { - branchName = branchName[:maximumBranchNameLength] - } - branchName = utils.CleanArgFromSpecSymbols(branchName) - branchName += "-dev" - - comment := IssuePRTtilePrefix + " '" + issueData.Title + "' " - body := "" - if len(issueData.Title) > 0 { - body = IssueSign + issueNumber + oneSpace + issueData.Title - } - notesObj, err := notesPkg.Serialize(issueURL, "", notesPkg.BranchTypeDev, issueData.Title) - if err != nil { - return "", nil, err - } - - return branchName, []string{comment, body, notesObj}, nil -} - func GetGithubIssueNameByNumber(issueNum string, parentrepo string) (string, error) { stdout, stderr, err := new(exec.PipedExec). Command("gh", "issue", "view", issueNum, "--repo", parentrepo, "--json", "title"). diff --git a/internal/commands/dev.go b/internal/commands/dev.go index e0ca753..5125a9e 100644 --- a/internal/commands/dev.go +++ b/internal/commands/dev.go @@ -29,7 +29,7 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s return deleteBranches(wd, parentRepo) } // qs dev is running - var branch string + var devBranchName string var notes []string var response string @@ -95,20 +95,12 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s return err } - issueInfo, err := issue.ParseIssueFromArgs(wd, args...) + issueInfo, err := issue.ParseIssueFromArgs(args...) if err != nil { return err } - switch issueInfo.Type { - case issue.GitHub: - branch, notes, err = gitcmds.BuildDevBranchName(issueInfo.URL) - case issue.Jira: - branch, notes, err = jira.GetJiraBranchName(args...) - default: - branch, notes, err = utils.GetBranchName(false, args...) - branch += "-dev" - } + devBranchName, notes, err = issue.BuildDevBranchName(issueInfo) if err != nil { if errors.Is(err, jira.ErrJiraIssueNotFoundOrInsufficientPermission) { fmt.Print(jira.NotFoundIssueOrInsufficientAccessRightSuggestion) @@ -117,17 +109,17 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s return err } - exists, err := branchExists(wd, branch) + exists, err := branchExists(wd, devBranchName) if err != nil { return fmt.Errorf("error checking branch existence: %w", err) } if exists { - return fmt.Errorf("dev branch '%s' already exists", branch) + return fmt.Errorf("dev branch '%s' already exists", devBranchName) } - cmd.SetContext(context.WithValue(cmd.Context(), utils.CtxKeyDevBranchName, branch)) + cmd.SetContext(context.WithValue(cmd.Context(), utils.CtxKeyDevBranchName, devBranchName)) - fmt.Print("Dev branch '" + branch + "' will be created. Continue(y/n)? ") + fmt.Print("Dev branch '" + devBranchName + "' will be created. Continue(y/n)? ") _, _ = fmt.Scanln(&response) switch response { @@ -145,12 +137,12 @@ func Dev(cmd *cobra.Command, wd string, doDelete bool, ignoreHook bool, args []s } } - if err := gitcmds.CreateDevBranch(wd, branch, mainBranch, notes); err != nil { + if err := gitcmds.CreateDevBranch(wd, devBranchName, mainBranch, notes); err != nil { return err } if issueInfo.Type == issue.GitHub { - notes, err = gitcmds.LinkBranchToGithubIssue(wd, parentRepo, issueInfo.URL, issueInfo.Number, branch, args...) + notes, err = gitcmds.LinkBranchToGithubIssue(wd, parentRepo, issueInfo.Text, issueInfo.ID, devBranchName, args...) if err != nil { return err } diff --git a/internal/issue/issue.go b/internal/issue/issue.go index b222682..9e76dfe 100644 --- a/internal/issue/issue.go +++ b/internal/issue/issue.go @@ -1,11 +1,17 @@ package issue import ( + "encoding/json" + "errors" "fmt" - "strconv" + "regexp" "strings" + "github.com/untillpro/goutils/exec" + "github.com/untillpro/goutils/logger" "github.com/untillpro/qs/internal/jira" + "github.com/untillpro/qs/internal/notes" + "github.com/untillpro/qs/utils" ) type IssueType int @@ -16,25 +22,138 @@ const ( Jira ) +const ( + maximumBranchNameLength = 100 + issuePRTitlePrefix = "Resolves issue" + issueSign = "Resolves #" +) + +// IssueInfo holds parsed issue metadata. +// ID format depends on Type: +// - GitHub: issue number (e.g. "42") +// - Jira: ticket key (e.g. "AIR-270") +// - FreeForm: empty type IssueInfo struct { - Type IssueType - URL string - ID string - Number int + Type IssueType + ID string + Text string } -func ParseIssueFromArgs(wd string, args ...string) (IssueInfo, error) { - url := args[0] // protected by caller side - if strings.Contains(url, "/issues/") { - segments := strings.Split(url, "/") - num, err := strconv.Atoi(segments[len(segments)-1]) - if err != nil { - return IssueInfo{}, fmt.Errorf("failed to convert issue number from string to int: %w", err) +func BuildDevBranchName(info IssueInfo) (devBranchName string, comments []string, err error) { + var title string + + switch info.Type { + case GitHub: + title, err = fetchGithubIssueTitle(info) + case Jira: + title, _, err = jira.GetJiraIssueTitle("", info.ID) + default: + title = info.Text + } + if err != nil { + return "", nil, err + } + + // Convert issue title to kebab-style branch name + devBranchName = titleToKebabWithPrefix(info.ID, title) + devBranchName = utils.CleanArgFromSpecSymbols(devBranchName) + + // Build notes (common for GitHub and Jira when title is available) + var githubURL, jiraURL string + switch info.Type { + case GitHub: + githubURL = info.Text + case Jira: + jiraURL = info.Text + } + notesObj, err := notes.Serialize(githubURL, jiraURL, notes.BranchTypeDev, title) + if err != nil { + return "", nil, err + } + + // Build comments + switch info.Type { + case GitHub: + comment := issuePRTitlePrefix + " '" + title + "' " + body := "" + if len(title) > 0 { + body = issueSign + info.ID + " " + title } - return IssueInfo{Type: GitHub, URL: url, ID: segments[len(segments)-1], Number: num}, nil + comments = []string{comment, body, notesObj} + case Jira: + if notesObj != "" { + comments = append(comments, notesObj) + } + comments = append(comments, "["+info.ID+"] "+title) + comments = append(comments, info.Text) + default: + comments = append(comments, title) + comments = append(comments, notesObj) + } + + devBranchName += "-dev" + + return devBranchName, comments, nil +} + +// titleToKebabWithPrefix converts an issue title into a kebab-case branch name prefixed with the issue ID. +func titleToKebabWithPrefix(id, title string) string { + kebabTitle := strings.ToLower(title) + kebabTitle = regexp.MustCompile(`[^a-z0-9]+`).ReplaceAllString(kebabTitle, "-") + kebabTitle = strings.Trim(kebabTitle, "-") + + branchName := fmt.Sprintf("%s-%s", id, kebabTitle) + if len(branchName) > maximumBranchNameLength { + branchName = branchName[:maximumBranchNameLength] + } + return branchName +} + +// fetchGithubIssueTitle fetches the issue title from GitHub. +func fetchGithubIssueTitle(info IssueInfo) (string, error) { + parts := strings.Split(info.Text, "/") + if len(parts) < 2 { + return "", fmt.Errorf("invalid issue URL format: %s", info.Text) + } + + repoURL := strings.Split(info.Text, "/issues/")[0] + urlParts := strings.Split(repoURL, "/") + if len(urlParts) < 5 { //nolint:revive + return "", fmt.Errorf("invalid GitHub URL format: %s", repoURL) + } + owner := urlParts[3] //nolint:revive + repo := urlParts[4] //nolint:revive + + stdout, stderr, err := new(exec.PipedExec). + Command("gh", "issue", "view", info.ID, "--repo", fmt.Sprintf("%s/%s", owner, repo), "--json", "title"). + RunToStrings() + if err != nil { + logger.Verbose(stderr) + if len(stderr) > 0 { + return "", errors.New(stderr) + } + return "", fmt.Errorf("failed to get issue title: %w", err) + } + logger.Verbose(stdout) + + var issueData struct { + Title string `json:"title"` + } + if err := json.Unmarshal([]byte(stdout), &issueData); err != nil { + return "", fmt.Errorf("failed to parse issue data: %w", err) + } + + return issueData.Title, nil +} + +func ParseIssueFromArgs(args ...string) (IssueInfo, error) { + text := args[0] // protected by caller side + if strings.Contains(text, "/issues/") { + segments := strings.Split(text, "/") + return IssueInfo{Type: GitHub, Text: text, ID: segments[len(segments)-1]}, nil } if id, ok := jira.GetJiraTicketIDFromArgs(args...); ok { - return IssueInfo{Type: Jira, URL: url, ID: id}, nil + return IssueInfo{Type: Jira, Text: text, ID: id}, nil } - return IssueInfo{Type: FreeForm}, nil + return IssueInfo{Type: FreeForm, Text: text}, nil } diff --git a/internal/issue/issue_test.go b/internal/issue/issue_test.go index 3c30b3a..e8782bc 100644 --- a/internal/issue/issue_test.go +++ b/internal/issue/issue_test.go @@ -12,12 +12,11 @@ func TestParseIssueFromArgs(t *testing.T) { args []string wantType IssueType wantID string - wantNum int wantErr bool }{ { name: "Multiple args returns FreeForm", - args: []string{"arg1", "arg2"}, + args: []string{"arg1"}, wantType: FreeForm, }, { @@ -36,7 +35,7 @@ func TestParseIssueFromArgs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require := require.New(t) - info, err := ParseIssueFromArgs(".", tt.args...) + info, err := ParseIssueFromArgs(tt.args...) if tt.wantErr { require.Error(err) return @@ -46,9 +45,74 @@ func TestParseIssueFromArgs(t *testing.T) { if tt.wantID != "" { require.Equal(tt.wantID, info.ID) } - if tt.wantNum != 0 { - require.Equal(tt.wantNum, info.Number) - } + }) + } +} + +func TestBuildDevBranchName(t *testing.T) { + tests := []struct { + name string + info IssueInfo + wantBranch string + }{ + { + name: "Single word", + info: IssueInfo{Type: FreeForm, Text: "Show"}, + wantBranch: "show-dev", + }, + { + name: "Multiple words", + info: IssueInfo{Type: FreeForm, Text: "Show must go on"}, + wantBranch: "show-must-go-on-dev", + }, + { + name: "Words with special chars", + info: IssueInfo{Type: FreeForm, Text: "Show ivv? must go on---"}, + wantBranch: "show-ivv-must-go-on-dev", + }, + { + name: "Text with PK URL", + info: IssueInfo{Type: FreeForm, Text: "Show must go on https://dev.heeus.io/launchpad/#!13427"}, + wantBranch: "show-must-go-on-https-dev-heeus-io-launchpad-13427-dev", + }, + { + name: "Text with special chars and PK URL", + info: IssueInfo{Type: FreeForm, Text: "Show ivv? must $ go on--- https://dev.heeus.io/launchpad/#!13427"}, + wantBranch: "show-ivv-must-go-on-https-dev-heeus-io-launchpad-1-dev", + }, + { + name: "Long text with PK URL", + info: IssueInfo{Type: FreeForm, Text: "Show me this very long string more than fifty symbols in lenth with long task number 11111111111111 https://dev.heeus.io/launchpad/#!13427"}, + wantBranch: "show-me-this-very-long-string-more-than-fifty-symb-dev", + }, + { + name: "PK URL only", + info: IssueInfo{Type: FreeForm, Text: "https://www.projectkaiser.com/online/#!3206802"}, + wantBranch: "https-www-projectkaiser-com-online-3206802-dev", + }, + { + name: "GitHub issues URL as FreeForm", + info: IssueInfo{Type: FreeForm, Text: "https://github.com/voedger/voedger/issues/395"}, + wantBranch: "https-github-com-voedger-voedger-issues-395-dev", + }, + { + name: "Short text with PK URL", + info: IssueInfo{Type: FreeForm, Text: "q dev https://dev.heeus.io/launchpad/#!13427"}, + wantBranch: "q-dev-https-dev-heeus-io-launchpad-13427-dev", + }, + { + name: "Long description with PK URL", + info: IssueInfo{Type: FreeForm, Text: "qs: add Kaiser task link to generated commit message https://dev.heeus.io/launchpad/#!25947"}, + wantBranch: "qs-add-kaiser-task-link-to-generated-commit-messag-dev", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + branch, _, err := BuildDevBranchName(tt.info) + require.NoError(err) + require.Equal(tt.wantBranch, branch) }) } } diff --git a/internal/jira/jira.go b/internal/jira/jira.go index 33a5d39..a3ef794 100644 --- a/internal/jira/jira.go +++ b/internal/jira/jira.go @@ -8,9 +8,6 @@ import ( "net/http" "os" "regexp" - - "github.com/untillpro/qs/internal/notes" - "github.com/untillpro/qs/utils" ) // GetJiraTicketIDFromArgs retrieves a JIRA ticket ID from the provided arguments. @@ -35,54 +32,11 @@ func GetJiraTicketIDFromArgs(args ...string) (jiraTicketID string, ok bool) { return "", false } -// GetJiraBranchName generates a branch name based on a JIRA issue URL in the arguments. -// If a JIRA URL is found, it generates a branch name in the format "-". -// Additionally, it generates comments in the format "[] ". -func GetJiraBranchName(args ...string) (branch string, comments []string, err error) { - comments = make([]string, 0, len(args)+1) // 1 for json notes - for _, arg := range args { - jiraTicketID, ok := GetJiraTicketIDFromArgs(arg) - if ok { - var brName string - issueName, _, err := GetJiraIssueName("", jiraTicketID) - if err != nil { - return "", nil, err - } - - if issueName == "" { - branch, _, err = utils.GetBranchName(false, args...) - if err != nil { - return "", nil, err - } - } else { - jiraTicketURL := arg // Full JIRA ticket URL - // Prepare new notes with issue name as description - notesObj, err := notes.Serialize("", jiraTicketURL, notes.BranchTypeDev, issueName) - if err != nil { - return "", nil, err - } - comments = append(comments, notesObj) - brName, _, err = utils.GetBranchName(false, issueName) - if err != nil { - return "", nil, err - } - branch = jiraTicketID + "-" + brName - } - comments = append(comments, "["+jiraTicketID+"] "+issueName) - } - } - // Add suffix "-dev" for a dev branch - branch += "-dev" - comments = append(comments, args...) - - return branch, comments, nil -} - -// GetJiraIssueName retrieves the name of a JIRA issue based on its ticket ID or URL. +// GetJiraIssueTitle retrieves the name of a JIRA issue based on its ticket ID or URL. // parameters: // - ticketURL: The URL of the JIRA ticket (optional). // - ticketID: The ID of the JIRA ticket (optional). -func GetJiraIssueName(ticketURL, ticketID string) (string, string, error) { +func GetJiraIssueTitle(ticketURL, ticketID string) (string, string, error) { // Validate the issue key if ticketID == "" { var ok bool diff --git a/uspecs/changes/archive/2602/2602231707-unify-dev-branch-name-build/change.md b/uspecs/changes/archive/2602/2602231707-unify-dev-branch-name-build/change.md new file mode 100644 index 0000000..dfb9dee --- /dev/null +++ b/uspecs/changes/archive/2602/2602231707-unify-dev-branch-name-build/change.md @@ -0,0 +1,23 @@ +--- +registered_at: 2026-02-23T14:55:04Z +change_id: 2602231455-unify-dev-branch-name-build +baseline: 73e6cc2002b16ac67ead7c88671a34dc21e01413 +archived_at: 2026-02-23T17:07:59Z +--- + +# Change request: Unify dev branch name building + +## Why + +Three separate functions (`BuildDevBranchName`, `GetJiraBranchName`, `GetBranchName` + manual `-dev` suffix) handle dev branch name construction depending on the issue type. This creates duplicated logic, scattered responsibilities, and the redundant `IssueInfo.Number` field (which is just `ID` converted to `int` and immediately converted back to `string` at the only call site). + +## What + +Combine `BuildDevBranchName`, `GetJiraBranchName`, and the freeform `GetBranchName`+`-dev` path into a single `BuildDevBranchName` function in the `issue` package that dispatches based on `IssueInfo.Type`: + +- Move dev branch name building logic from `gitcmds.BuildDevBranchName` (GitHub), `jira.GetJiraBranchName` (Jira), and the freeform path in `dev.go` into `issue.BuildDevBranchName(info IssueInfo, args ...string)` +- Remove `IssueInfo.Number` field — use `ID` (string) only +- Add doc comment to `ID` field describing format per source type (GitHub: issue number, Jira: ticket key like `AIR-XXXX`, FreeForm: empty) +- Update `LinkBranchToGithubIssue` to accept `string` instead of `int` for issue number +- Remove now-unused `GetJiraBranchName` from `jira` package and `BuildDevBranchName` from `gitcmds` package +- Simplify `dev.go` caller to a single `issue.BuildDevBranchName` call diff --git a/uspecs/changes/archive/2602/2602231707-unify-dev-branch-name-build/impl.md b/uspecs/changes/archive/2602/2602231707-unify-dev-branch-name-build/impl.md new file mode 100644 index 0000000..fed184f --- /dev/null +++ b/uspecs/changes/archive/2602/2602231707-unify-dev-branch-name-build/impl.md @@ -0,0 +1,57 @@ +# Implementation plan: Unify dev branch name building + +## Construction + +### Core type changes + +- [x] update: [internal/issue/issue.go](../../../../../internal/issue/issue.go) + - remove: `Number` field from `IssueInfo`, rename `URL` to `Text` + - add: Doc comment to `ID` field (GitHub: issue number, Jira: ticket key like `AIR-XXXX`, FreeForm: empty) + - remove: `strconv.Atoi` conversion and `Number` assignment in `ParseIssueFromArgs` + - remove: `wd` parameter from `ParseIssueFromArgs` + - add: `BuildDevBranchName(info IssueInfo) (string, []string, error)` that dispatches based on `IssueInfo.Type` + - add: `titleToKebab(id, title string)` helper + - add: `fetchGithubIssueTitle(info IssueInfo)` helper (moved from `gitcmds`) + +### Signature changes + +- [x] update: [gitcmds/github.go](../../../../../gitcmds/github.go) + - update: `LinkBranchToGithubIssue` parameter `issueNumber` from `int` to `string` + - remove: `strconv.Itoa(issueNumber)` conversion inside `LinkBranchToGithubIssue` + - remove: `BuildDevBranchName` function (logic moves to `issue.BuildDevBranchName`) + +- [x] update: [internal/jira/jira.go](../../../../../internal/jira/jira.go) + - remove: `GetJiraBranchName` function (logic moves to `issue.BuildDevBranchName`) + - rename: `GetJiraIssueName` → `GetJiraIssueTitle` + +### Caller updates + +- [x] update: [internal/commands/dev.go](../../../../../internal/commands/dev.go) + - update: Replace three-way `switch` with single `issue.BuildDevBranchName` call + - update: `LinkBranchToGithubIssue` call to pass `issueInfo.ID` (string) instead of `issueInfo.Number` (int) + - update: `ParseIssueFromArgs` call without `wd` parameter + - update: Use `issueInfo.Text` instead of `issueInfo.URL` + +- [x] update: [gitcmds/gitcmds.go](../../../../../gitcmds/gitcmds.go) + - update: `GetJiraIssueName` → `GetJiraIssueTitle` call in `GetIssueDescription` + +### Removed files + +- [x] delete: [utils/branch.go](../../../../../utils/branch.go) + - remove: `GetBranchName`, `GetTaskIDFromURL`, `splitQuotedArgs`, `clearEmptyArgs` (all unused after refactoring) + +### Tests + +- [x] update: [internal/issue/issue_test.go](../../../../../internal/issue/issue_test.go) + - remove: `wantNum` field and `Number` assertions + - update: `ParseIssueFromArgs` calls without `wd` parameter + - add: `TestBuildDevBranchName` with 10 FreeForm test cases (relocated from `TestGetBranchName`) + +- [x] delete: [utils/branch_test.go](../../../../../utils/branch_test.go) + - remove: `TestGetBranchName` (relocated to `issue_test.go` as `TestBuildDevBranchName`) + - remove: `TestGeRepoNameFromURL` (referenced non-existent `GetTaskIDFromURL`) + +- [x] review: Verify no regressions + - `go build ./...` compiles without errors + - `go test ./...` — all tests pass + diff --git a/utils/branch.go b/utils/branch.go deleted file mode 100644 index 3d43648..0000000 --- a/utils/branch.go +++ /dev/null @@ -1,93 +0,0 @@ -package utils - -import ( - "errors" - "strings" - - "github.com/untillpro/qs/internal/notes" -) - -func GetBranchName(ignoreEmptyArg bool, args ...string) (branch string, comments []string, err error) { - - args = clearEmptyArgs(args) - if len(args) == 0 { - if ignoreEmptyArg { - return "", []string{}, nil - } - - return "", []string{}, errors.New("need branch name for dev") - } - - newArgs := splitQuotedArgs(args...) - comments = make([]string, 0, len(newArgs)+1) // 1 for json notes - comments = append(comments, newArgs...) - - // Store the original description (joined arguments) for PR title and commit message - description := strings.Join(newArgs, " ") - - for i, arg := range newArgs { - arg = strings.TrimSpace(arg) - if i == 0 { - branch = arg - continue - } - if i == len(newArgs)-1 { - // Retrieve taskID from url and add it first to branch name - url := arg - topicID := GetTaskIDFromURL(url) - if topicID == arg { - branch = branch + "-" + topicID - } else { - branch = topicID + "-" + branch - } - break - } - branch = branch + "-" + arg - } - branch = CleanArgFromSpecSymbols(branch) - // Prepare new notes with description - notesObj, err := notes.Serialize("", "", notes.BranchTypeDev, description) - if err != nil { - return "", []string{}, err - } - comments = append(comments, notesObj) - - return branch, comments, nil -} - -func clearEmptyArgs(args []string) (newargs []string) { - for _, arg := range args { - arg = strings.TrimSpace(arg) - if len(arg) > 0 { - newargs = append(newargs, arg) - } - } - return -} - -func splitQuotedArgs(args ...string) []string { - var newargs []string - for _, arg := range args { - subargs := strings.Split(arg, " ") - if len(subargs) == 0 { - continue - } - for _, a := range subargs { - if len(a) > 0 { - newargs = append(newargs, a) - } - } - } - return newargs -} - -func GetTaskIDFromURL(url string) string { - var entry string - str := strings.Split(url, "/") - if len(str) > 0 { - entry = str[len(str)-1] - } - entry = strings.ReplaceAll(entry, "#", "") - entry = strings.ReplaceAll(entry, "!", "") - return strings.TrimSpace(entry) -} diff --git a/utils/branch_test.go b/utils/branch_test.go deleted file mode 100644 index f4cf6b2..0000000 --- a/utils/branch_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package utils - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetBranchName(t *testing.T) { - str, _, err := GetBranchName(false, "Show", "must", "go", "on", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-must-go-on", str) - - str, _, err = GetBranchName(false, "Show ivv?", "must ", "go", "on---", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-ivv-must-go-on", str) - - str, _, err = GetBranchName(false, "Show", "must", "go", "on") - require.NoError(t, err) - require.Equal(t, "Show-must-go-on", str) - - str, _, err = GetBranchName(false, "Show") - require.NoError(t, err) - require.Equal(t, "Show", str) - - str, _, err = GetBranchName(false, "Show ivv? must $ go on--- https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-ivv-must-go-on", str) - - str, _, err = GetBranchName(false, "Show ivv? must $ ", "go on--- https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-ivv-must-go-on", str) - - str, _, err = GetBranchName(false, "Show ivv? must $ go on---", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-ivv-must-go-on", str) - - str, _, err = GetBranchName(false, "Show", "ivv? must $ go on--- https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-ivv-must-go-on", str) - - str, _, err = GetBranchName(false, "Show", "ivv? must $ go on---", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-ivv-must-go-on", str) - - str, _, err = GetBranchName(false, "q", "dev", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-q-dev", str) - - str, _, err = GetBranchName(false, "q", "dev", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-q-dev", str) - - str, _, err = GetBranchName(false, "qs: add Kaiser task link to generated commit message", "https://dev.heeus.io/launchpad/#!25947") - require.NoError(t, err) - require.Equal(t, "25947-qs-add-Kaiser-task-link-to-generated-commit", str) - - //Logn name - str, _, err = GetBranchName(false, "Show", "me this very long string more than fifty symbols in lenth with long task number 11111111111111", "https://dev.heeus.io/launchpad/#!13427") - require.NoError(t, err) - require.Equal(t, "13427-Show-me-this-very-long-string-more-than-fift", str) - - //URL name - str, _, err = GetBranchName(false, "https://www.projectkaiser.com/online/#!3206802") - require.NoError(t, err) - require.Equal(t, "www-projectkaiser-com-online-#-3206802", str) - - str, _, err = GetBranchName(false, "https://github.com/voedger/voedger/issues/395") - require.NoError(t, err) - require.Equal(t, "github-com-voedger-voedger-issues-395", str) -} - -func TestGeRepoNameFromURL(t *testing.T) { - topicid := GetTaskIDFromURL("https://dev.heeus.io/launchpad/#!13427") - assert.Equal(t, "13427", topicid) -}