Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ plural*.o

# Vendored dependencies
vendor/

# Agents
.codex/
4 changes: 3 additions & 1 deletion cmd/command/plural/plural.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/pluralsh/plural-cli/cmd/command/stacks"
"github.com/pluralsh/plural-cli/cmd/command/up"
"github.com/pluralsh/plural-cli/cmd/command/version"
"github.com/pluralsh/plural-cli/cmd/command/workbenches"
"github.com/pluralsh/plural-cli/pkg/client"
"github.com/pluralsh/plural-cli/pkg/common"
conf "github.com/pluralsh/plural-cli/pkg/config"
Expand Down Expand Up @@ -103,7 +104,7 @@ func CreateNewApp(plural *Plural) *cli.App {
app.Usage = "Tooling to manage your installed plural applications"
app.EnableBashCompletion = true
app.Flags = globalFlags()
commands := make([]cli.Command, 0, 15+len(plural.getCommands()))
commands := make([]cli.Command, 0, 16+len(plural.getCommands()))
commands = append(commands,
api.Command(plural.Plural),
agentscmd.Command(plural.Plural),
Expand All @@ -121,6 +122,7 @@ func CreateNewApp(plural *Plural) *cli.App {
cmdinit.Command(plural.Plural),
up.Command(plural.Plural),
version.Command(),
workbenches.Command(plural.Plural),
)
commands = append(commands, plural.getCommands()...)
app.Commands = commands
Expand Down
9 changes: 5 additions & 4 deletions cmd/command/stacks/stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"fmt"

"github.com/AlecAivazis/survey/v2"
"github.com/samber/lo"
"github.com/urfave/cli"

"github.com/pluralsh/plural-cli/pkg/api"
"github.com/pluralsh/plural-cli/pkg/client"
"github.com/pluralsh/plural-cli/pkg/common"
"github.com/pluralsh/plural-cli/pkg/config"
"github.com/pluralsh/plural-cli/pkg/stacks"
"github.com/pluralsh/plural-cli/pkg/utils/git"
"github.com/samber/lo"
"github.com/urfave/cli"
)

func init() {
Expand Down Expand Up @@ -83,9 +84,9 @@ func (p *Plural) handleGenerateBackend(_ *cli.Context) error {
}

stackNames := make(map[string]string)
infrastructureStacks, err := p.ConsoleClient.ListaStacks()
infrastructureStacks, err := p.ConsoleClient.ListStacks()
if err != nil {
return api.GetErrorResponse(err, "ListaStacks")
return api.GetErrorResponse(err, "ListStacks")
}
if infrastructureStacks == nil || infrastructureStacks.InfrastructureStacks == nil || len(infrastructureStacks.InfrastructureStacks.Edges) == 0 {
return fmt.Errorf("returned objects list [ListStacks] is nil")
Expand Down
82 changes: 82 additions & 0 deletions cmd/command/workbenches/pr_followup_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package workbenches

import (
"fmt"
"strings"
"time"

"github.com/pluralsh/plural-cli/pkg/console"
)

const pullRequestNotFoundError = "pull request not found"

type PullRequestURLResolver interface {
Resolve(options PullRequestOptions) (string, error)
}

type PRFollowupService struct {
client console.ConsoleClient
resolver PullRequestURLResolver
}

type PRFollowupOptions struct {
Prompt string
Defer time.Duration
PullRequest PullRequestOptions
SkipMissing bool
}

type PRFollowupResult struct {
PromptID string
PullRequestURL string
WorkbenchJobURL string
Skipped bool
}

func NewPRFollowupService(client console.ConsoleClient, resolver PullRequestURLResolver) *PRFollowupService {
return &PRFollowupService{client: client, resolver: resolver}
}

func (s *PRFollowupService) Create(options PRFollowupOptions) (PRFollowupResult, error) {
if strings.TrimSpace(options.Prompt) == "" {
return PRFollowupResult{}, fmt.Errorf("prompt cannot be empty")
}
if s.client == nil {
return PRFollowupResult{}, fmt.Errorf("workbench PR follow-up client is not configured")
}
if s.resolver == nil {
return PRFollowupResult{}, fmt.Errorf("pull request URL resolver is not configured")
}

pullRequestURL, err := s.resolver.Resolve(options.PullRequest)
if err != nil {
return PRFollowupResult{}, err
}

result, err := s.client.EnqueueWorkbenchPRFollowup(pullRequestURL, options.Prompt, options.Defer)
if err != nil {
if options.SkipMissing && isPullRequestNotFound(err) {
return PRFollowupResult{PullRequestURL: pullRequestURL, Skipped: true}, nil
}

return PRFollowupResult{}, err
}

return PRFollowupResult{
PromptID: result.GetID(),
PullRequestURL: pullRequestURL,
WorkbenchJobURL: result.GetWorkbenchJob().GetURL(),
}, nil
}

func isPullRequestNotFound(err error) bool {
if err == nil {
return false
}

message := err.Error()
return message == pullRequestNotFoundError ||
strings.HasPrefix(message, pullRequestNotFoundError+":") ||
strings.HasSuffix(message, ": "+pullRequestNotFoundError) ||
strings.Contains(message, ": "+pullRequestNotFoundError+":")
}
83 changes: 83 additions & 0 deletions cmd/command/workbenches/pull_request_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package workbenches

import (
"regexp"
"strings"
)

type ProviderName string

const (
ProviderAuto ProviderName = "auto"
ProviderGitHub ProviderName = "github"
ProviderGitLab ProviderName = "gitlab"
ProviderBitbucket ProviderName = "bitbucket"
)

type PullRequestProvider interface {
Name() ProviderName
Supports(host string) bool
PullRequestNumber(subject string) (string, bool)
PullRequestURL(repositoryURL, number string) string
}

type pullRequestProvider struct {
name ProviderName
hostMarker string
path string
patterns []*regexp.Regexp
}

func defaultPullRequestProviders() []PullRequestProvider {
return []PullRequestProvider{
pullRequestProvider{
name: ProviderGitHub,
hostMarker: "github",
path: "/pull/",
patterns: []*regexp.Regexp{
regexp.MustCompile(`\(#([0-9]+)\)`),
regexp.MustCompile(`(?i)merge pull request #([0-9]+)\b`),
},
},
pullRequestProvider{
name: ProviderGitLab,
hostMarker: "gitlab",
path: "/-/merge_requests/",
patterns: []*regexp.Regexp{
regexp.MustCompile(`!([0-9]+)\b`),
},
},
pullRequestProvider{
name: ProviderBitbucket,
hostMarker: "bitbucket",
path: "/pull-requests/",
patterns: []*regexp.Regexp{
regexp.MustCompile(`(?i)pull request #([0-9]+)\b`),
},
},
}
}

func (p pullRequestProvider) Name() ProviderName {
return p.name
}

func (p pullRequestProvider) Supports(host string) bool {
return strings.Contains(strings.ToLower(host), p.hostMarker)
}

func (p pullRequestProvider) PullRequestNumber(subject string) (string, bool) {
for _, pattern := range p.patterns {
matches := pattern.FindStringSubmatch(subject)

if len(matches) == 2 {
return matches[1], true
}
}

return "", false
}

func (p pullRequestProvider) PullRequestURL(repositoryURL, number string) string {
return repositoryURL + p.path + number
}
18 changes: 18 additions & 0 deletions cmd/command/workbenches/pull_request_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package workbenches

import gitutils "github.com/pluralsh/plural-cli/pkg/utils/git"

type PullRequestRepository interface {
CommitSubject(ref string) (string, error)
RemoteURL() (string, error)
}

type GitPullRequestRepository struct{}

func (GitPullRequestRepository) CommitSubject(ref string) (string, error) {
return gitutils.GitRaw("show", "-s", "--format=%s", ref)
}

func (GitPullRequestRepository) RemoteURL() (string, error) {
return gitutils.GitRaw("remote", "get-url", "origin")
}
Loading
Loading