91 lines
3.0 KiB
Go
91 lines
3.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func (c *commandContext) resolvePRRef(ctx context.Context, ref string, project projectDetails) (string, error) {
|
|
ref = strings.TrimSpace(ref)
|
|
if ref == "" {
|
|
return "", usageError{errors.New("PR reference must be a github.com PR URL or a number")}
|
|
}
|
|
if isNumericPRRef(ref) {
|
|
repo := strings.TrimSpace(project.Repo)
|
|
if repo == "" {
|
|
// The daemon must not shell out to external CLIs from its loopback API;
|
|
// when the durable project record lacks repo_origin_url, the thin CLI
|
|
// does the one-off gh lookup from the registered project checkout and
|
|
// sends the daemon a normalized URL.
|
|
out, err := c.deps.CommandOutputInDir(ctx, project.Path, "gh", "repo", "view", "--json", "url", "-q", ".url")
|
|
if err != nil || strings.TrimSpace(string(out)) == "" {
|
|
return "", usageError{errors.New("gh not available; pass the full PR URL")}
|
|
}
|
|
repo = strings.TrimSpace(string(out))
|
|
}
|
|
owner, name, err := cliGitHubRepoFromURL(repo)
|
|
if err != nil {
|
|
return "", usageError{errors.New("PR reference must be a github.com PR URL or a number")}
|
|
}
|
|
n, _ := strconv.Atoi(strings.TrimPrefix(ref, "#"))
|
|
return fmt.Sprintf("https://github.com/%s/%s/pull/%d", owner, name, n), nil
|
|
}
|
|
owner, name, n, err := cliParseGitHubPRURL(ref)
|
|
if err != nil || owner == "" || name == "" || n <= 0 {
|
|
return "", usageError{errors.New("PR reference must be a github.com PR URL or a number")}
|
|
}
|
|
return fmt.Sprintf("https://github.com/%s/%s/pull/%d", owner, name, n), nil
|
|
}
|
|
|
|
func isNumericPRRef(ref string) bool {
|
|
ref = strings.TrimPrefix(strings.TrimSpace(ref), "#")
|
|
n, err := strconv.Atoi(ref)
|
|
return err == nil && n > 0
|
|
}
|
|
|
|
func cliParseGitHubPRURL(raw string) (string, string, int, error) {
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return "", "", 0, err
|
|
}
|
|
if !strings.EqualFold(u.Scheme, "https") || !strings.EqualFold(u.Hostname(), "github.com") {
|
|
return "", "", 0, errors.New("not github")
|
|
}
|
|
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
|
|
if len(parts) != 4 || parts[2] != "pull" {
|
|
return "", "", 0, errors.New("not pr")
|
|
}
|
|
n, err := strconv.Atoi(parts[3])
|
|
if err != nil || n <= 0 {
|
|
return "", "", 0, errors.New("bad number")
|
|
}
|
|
return parts[0], strings.TrimSuffix(parts[1], ".git"), n, nil
|
|
}
|
|
|
|
func cliGitHubRepoFromURL(raw string) (string, string, error) {
|
|
raw = strings.TrimSpace(raw)
|
|
if strings.HasPrefix(raw, "git@github.com:") {
|
|
parts := strings.Split(strings.TrimSuffix(strings.TrimPrefix(raw, "git@github.com:"), ".git"), "/")
|
|
if len(parts) == 2 && parts[0] != "" && parts[1] != "" {
|
|
return parts[0], parts[1], nil
|
|
}
|
|
return "", "", errors.New("bad repo")
|
|
}
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if !strings.EqualFold(u.Hostname(), "github.com") {
|
|
return "", "", errors.New("not github")
|
|
}
|
|
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
|
|
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
|
|
return "", "", errors.New("bad repo")
|
|
}
|
|
return parts[0], strings.TrimSuffix(parts[1], ".git"), nil
|
|
}
|