Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSCI Various Fixes #1189

Closed
wants to merge 9 commits into from
Closed
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
30 changes: 30 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml

trigger:
- master

pool:
vmImage: 'ubuntu-latest'

steps:
- task: GoTool@0
inputs:
version: '1.14'
- task: Bash@3
inputs:
targetType: 'inline'
script: 'go mod vendor && make build-service'
env:
GOFLAGS: "-mod=vendor"
- task: Docker@2
inputs:
containerRegistry: 'atlantis-creg'
repository: 'upstreamatlantis'
command: 'buildAndPush'
Dockerfile: './Dockerfile'
tags: |
$(Build.BuildId)
latest
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ require (
github.com/imdario/mergo v0.3.5 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/magiconair/properties v1.7.3 // indirect
github.com/mcdafydd/go-azuredevops v0.11.1
github.com/mcdafydd/go-azuredevops v0.11.2
github.com/microcosm-cc/bluemonday v1.0.1
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
github.com/mitchellh/go-homedir v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mcdafydd/go-azuredevops v0.11.1 h1:NO4wlkyFpdxqZZzNzn5m3fJc4box0jnkC8LBhAaPXeA=
github.com/mcdafydd/go-azuredevops v0.11.1/go.mod h1:B4UDyn7WEj1/97f45j3VnzEfkWKe05+/dCcAPdOET4A=
github.com/mcdafydd/go-azuredevops v0.11.2 h1:M8t1io1bfUD7N1ZQHJVk3DvvEBy41FNkYgIwWoQnywk=
github.com/mcdafydd/go-azuredevops v0.11.2/go.mod h1:B4UDyn7WEj1/97f45j3VnzEfkWKe05+/dCcAPdOET4A=
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 h1:KHyL+3mQOF9sPfs26lsefckcFNDcIZtiACQiECzIUkw=
Expand Down
13 changes: 11 additions & 2 deletions server/events/command_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHead
c.updateCommitStatus(ctx, cmd.Name, pullStatus)

if cmd.Name == models.ApplyCommand && c.automergeEnabled(ctx, projectCmds) {
c.automerge(ctx, pullStatus)
c.automerge(ctx, pullStatus, c.deleteSourceBranchOnMerge(ctx, projectCmds))

}
}

Expand Down Expand Up @@ -371,7 +372,7 @@ func (c *DefaultCommandRunner) updateCommitStatus(ctx *CommandContext, cmd model
}
}

func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.PullStatus) {
func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.PullStatus, shouldDeleteSourceBranch bool) {
// We only automerge if all projects have been successfully applied.
for _, p := range pullStatus.Projects {
if p.Status != models.AppliedPlanStatus {
Expand All @@ -388,6 +389,9 @@ func (c *DefaultCommandRunner) automerge(ctx *CommandContext, pullStatus models.

// Make the API call to perform the merge.
ctx.Log.Info("automerging pull request")
if shouldDeleteSourceBranch {
ctx.Pull.DeleteSourceBranchOnMerge = true
}
err := c.VCSClient.MergePull(ctx.Pull)

if err != nil {
Expand Down Expand Up @@ -592,6 +596,11 @@ func (c *DefaultCommandRunner) automergeEnabled(ctx *CommandContext, projectCmds
// Otherwise we check if this repo is configured for automerging.
(len(projectCmds) > 0 && projectCmds[0].AutomergeEnabled)
}
// deleteSourceBranchOnMerge returns true if we should delete the source branch on merge in this context.
func (c *DefaultCommandRunner) deleteSourceBranchOnMerge(ctx *CommandContext, projectCmds []models.ProjectCommandContext) bool {
//check if this repo is configured for automerging.
return (len(projectCmds) > 0 && projectCmds[0].DeleteSourceBranchOnMerge)
}

// parallelApplyEnabled returns true if parallel apply is enabled in this context.
func (c *DefaultCommandRunner) parallelApplyEnabled(ctx *CommandContext, projectCmds []models.ProjectCommandContext) bool {
Expand Down
8 changes: 3 additions & 5 deletions server/events/commit_status_updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ type DefaultCommitStatusUpdater struct {
}

func (d *DefaultCommitStatusUpdater) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error {
src := fmt.Sprintf("%s/%s", d.StatusName, command.String())
var descripWords string
switch status {
case models.PendingCommitStatus:
Expand All @@ -56,24 +55,23 @@ func (d *DefaultCommitStatusUpdater) UpdateCombined(repo models.Repo, pull model
descripWords = "succeeded."
}
descrip := fmt.Sprintf("%s %s", strings.Title(command.String()), descripWords)
return d.Client.UpdateStatus(repo, pull, status, src, descrip, "")
return d.Client.UpdateStatus(repo, pull, status, command.String(), descrip, "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will change the form of the src argument from atlantis/<command> to just the command name. This src field is used by most of the VCS plugins to drive a data field that is sent along in the PR status update requests. Changing the format of this field is going to be a breaking change to anyone with a PR policy keyed off of this particular string.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been fighting #1172 as well and it's gotten to a point where we've had to disable the status policies altogether and rely on reviewers only. I'd like to 2nd what @acastle said, if I can help in any way to getting this merged let me know.

Would it make sense to use a different string just for DevOps users? It's already broken for them anyway and prevents breaking changes to other VCS users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jack64 I have submitted a separate PR #1209 that resolves just the status checks which was merged last week. There hasn't been a new release cut yet but if you do a new build off of master you should be all set.

}

func (d *DefaultCommitStatusUpdater) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) error {
src := fmt.Sprintf("%s/%s", d.StatusName, command.String())
cmdVerb := "planned"
if command == models.ApplyCommand {
cmdVerb = "applied"
}
return d.Client.UpdateStatus(repo, pull, status, src, fmt.Sprintf("%d/%d projects %s successfully.", numSuccess, numTotal, cmdVerb), "")
return d.Client.UpdateStatus(repo, pull, status, command.String(), fmt.Sprintf("%d/%d projects %s successfully.", numSuccess, numTotal, cmdVerb), "")
}

func (d *DefaultCommitStatusUpdater) UpdateProject(ctx models.ProjectCommandContext, cmdName models.CommandName, status models.CommitStatus, url string) error {
projectID := ctx.ProjectName
if projectID == "" {
projectID = fmt.Sprintf("%s/%s", ctx.RepoRelDir, ctx.Workspace)
}
src := fmt.Sprintf("%s/%s: %s", d.StatusName, cmdName.String(), projectID)
src := fmt.Sprintf("%s: %s", cmdName.String(), projectID)
var descripWords string
switch status {
case models.PendingCommitStatus:
Expand Down
14 changes: 6 additions & 8 deletions server/events/commit_status_updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ func TestUpdateCombined(t *testing.T) {
err := s.UpdateCombined(models.Repo{}, models.PullRequest{}, c.status, c.command)
Ok(t, err)

expSrc := fmt.Sprintf("atlantis/%s", c.command)
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, expSrc, c.expDescrip, "")
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, c.command, c.expDescrip, "")
})
}
}
Expand Down Expand Up @@ -136,8 +135,7 @@ func TestUpdateCombinedCount(t *testing.T) {
err := s.UpdateCombinedCount(models.Repo{}, models.PullRequest{}, c.status, c.command, c.numSuccess, c.numTotal)
Ok(t, err)

expSrc := fmt.Sprintf("%s/%s", s.StatusName, c.command)
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, expSrc, c.expDescrip, "")
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, c.command, c.expDescrip, "")
})
}
}
Expand All @@ -156,13 +154,13 @@ func TestDefaultCommitStatusUpdater_UpdateProjectSrc(t *testing.T) {
projectName: "name",
repoRelDir: ".",
workspace: "default",
expSrc: "atlantis/plan: name",
expSrc: "plan: name",
},
{
projectName: "",
repoRelDir: "dir1/dir2",
workspace: "workspace",
expSrc: "atlantis/plan: dir1/dir2/workspace",
expSrc: "plan: dir1/dir2/workspace",
},
}

Expand Down Expand Up @@ -236,7 +234,7 @@ func TestDefaultCommitStatusUpdater_UpdateProject(t *testing.T) {
c.status,
"url")
Ok(t, err)
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, fmt.Sprintf("atlantis/%s: ./default", c.cmd.String()), c.expDescrip, "url")
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, fmt.Sprintf("%s: ./default", c.cmd.String()), c.expDescrip, "url")
})
}
}
Expand All @@ -255,5 +253,5 @@ func TestDefaultCommitStatusUpdater_UpdateProjectCustomStatusName(t *testing.T)
"url")
Ok(t, err)
client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{},
models.SuccessCommitStatus, "custom/apply: ./default", "Apply succeeded.", "url")
models.SuccessCommitStatus, "apply: ./default", "Apply succeeded.", "url")
}
4 changes: 4 additions & 0 deletions server/events/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ type PullRequest struct {
State PullRequestState
// BaseRepo is the repository that the pull request will be merged into.
BaseRepo Repo
// DeleteSourceBranchOnMerge will set a field that tells the VCS to delete the source branch when the PR is merged
DeleteSourceBranchOnMerge bool
}

type PullRequestState int
Expand Down Expand Up @@ -317,6 +319,8 @@ type ProjectCommandContext struct {
AutoplanEnabled bool
// BaseRepo is the repository that the pull request will be merged into.
BaseRepo Repo
// DeleteSourceBranchOnMerge will attempt to allow a branch to be deleted when merged (AzureDevOps Support Only)
DeleteSourceBranchOnMerge bool
// EscapedCommentArgs are the extra arguments that were added to the atlantis
// command, ex. atlantis plan -- -target=resource. We then escape them
// by adding a \ before each character so that they can be used within
Expand Down
21 changes: 9 additions & 12 deletions server/events/project_command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const (
DefaultParallelApplyEnabled = false
// DefaultParallelPlanEnabled is the default for the parallel plan setting.
DefaultParallelPlanEnabled = false
// DefaultDeleteSourceBranchOnMerge being false is the default setting whether or not to remove a source branch on merge
DefaultDeleteSourceBranchOnMerge = false
)

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_project_command_builder.go ProjectCommandBuilder
Expand Down Expand Up @@ -174,7 +176,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext,
for _, mp := range matchingProjects {
ctx.Log.Debug("determining config for project at dir: %q workspace: %q", mp.Dir, mp.Workspace)
mergedCfg := p.GlobalCfg.MergeProjectCfg(ctx.Log, ctx.BaseRepo.ID(), mp, repoCfg)
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, mergedCfg, commentFlags, repoCfg.Automerge, repoCfg.ParallelApply, repoCfg.ParallelPlan, verbose, repoDir))
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, mergedCfg, commentFlags, repoCfg.Automerge, repoCfg.ParallelApply, repoCfg.ParallelPlan, verbose, repoDir, repoCfg.DeleteSourceBranchOnMerge))
}
} else {
// If there is no config file, then we'll plan each project that
Expand All @@ -185,7 +187,7 @@ func (p *DefaultProjectCommandBuilder) buildPlanAllCommands(ctx *CommandContext,
for _, mp := range modifiedProjects {
ctx.Log.Debug("determining config for project at dir: %q", mp.Path)
pCfg := p.GlobalCfg.DefaultProjCfg(ctx.Log, ctx.BaseRepo.ID(), mp.Path, DefaultWorkspace)
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, pCfg, commentFlags, DefaultAutomergeEnabled, DefaultParallelApplyEnabled, DefaultParallelPlanEnabled, verbose, repoDir))
projCtxs = append(projCtxs, p.buildCtx(ctx, models.PlanCommand, pCfg, commentFlags, DefaultAutomergeEnabled, DefaultParallelApplyEnabled, DefaultParallelPlanEnabled, verbose, repoDir, DefaultDeleteSourceBranchOnMerge))
}
}

Expand Down Expand Up @@ -320,12 +322,14 @@ func (p *DefaultProjectCommandBuilder) buildProjectCommandCtx(
automerge := DefaultAutomergeEnabled
parallelApply := DefaultParallelApplyEnabled
parallelPlan := DefaultParallelPlanEnabled
deleteBranchOnMerge := DefaultDeleteSourceBranchOnMerge
if repoCfgPtr != nil {
automerge = repoCfgPtr.Automerge
parallelApply = repoCfgPtr.ParallelApply
parallelPlan = repoCfgPtr.ParallelPlan
deleteBranchOnMerge = repoCfgPtr.DeleteSourceBranchOnMerge
}
return p.buildCtx(ctx, cmd, projCfg, commentFlags, automerge, parallelApply, parallelPlan, verbose, repoDir), nil
return p.buildCtx(ctx, cmd, projCfg, commentFlags, automerge, parallelApply, parallelPlan, verbose, repoDir, deleteBranchOnMerge), nil
}

// getCfg returns the atlantis.yaml config (if it exists) for this project. If
Expand Down Expand Up @@ -409,15 +413,7 @@ func (p *DefaultProjectCommandBuilder) validateWorkspaceAllowed(repoCfg *valid.R
}

// buildCtx is a helper method that handles constructing the ProjectCommandContext.
func (p *DefaultProjectCommandBuilder) buildCtx(ctx *CommandContext,
cmd models.CommandName,
projCfg valid.MergedProjectCfg,
commentArgs []string,
automergeEnabled bool,
parallelApplyEnabled bool,
parallelPlanEnabled bool,
verbose bool,
absRepoDir string) models.ProjectCommandContext {
func (p *DefaultProjectCommandBuilder) buildCtx(ctx *CommandContext, cmd models.CommandName, projCfg valid.MergedProjectCfg, commentArgs []string, automergeEnabled bool, parallelApplyEnabled bool, parallelPlanEnabled bool, verbose bool, absRepoDir string, deleteBranchOnMerge bool) models.ProjectCommandContext {

var steps []valid.Step
switch cmd {
Expand All @@ -438,6 +434,7 @@ func (p *DefaultProjectCommandBuilder) buildCtx(ctx *CommandContext,
BaseRepo: ctx.BaseRepo,
EscapedCommentArgs: p.escapeArgs(commentArgs),
AutomergeEnabled: automergeEnabled,
DeleteSourceBranchOnMerge: deleteBranchOnMerge,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs fmt

ParallelApplyEnabled: parallelApplyEnabled,
ParallelPlanEnabled: parallelPlanEnabled,
AutoplanEnabled: projCfg.AutoplanEnabled,
Expand Down
28 changes: 17 additions & 11 deletions server/events/vcs/azuredevops_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (g *AzureDevopsClient) UpdateStatus(repo models.Repo, pull models.PullReque
adState = azuredevops.GitFailed.String()
}

genreStr := "Atlantis Bot"
genreStr := "Atlantis Bot/atlantis"
status := azuredevops.GitPullRequestStatus{}
status.Context = &azuredevops.GitStatusContext{
Name: &src,
Expand Down Expand Up @@ -289,22 +289,15 @@ func (g *AzureDevopsClient) UpdateStatus(repo models.Repo, pull models.PullReque
// until we handle branch policies
// https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops
func (g *AzureDevopsClient) MergePull(pull models.PullRequest) error {
descriptor := "Atlantis Terraform Pull Request Automation"
i := "atlantis"
imageURL := "https://github.com/runatlantis/atlantis/raw/master/runatlantis.io/.vuepress/public/hero.png"
id := azuredevops.IdentityRef{
Descriptor: &descriptor,
ID: &i,
ImageURL: &imageURL,
}

// Set default pull request completion options
mcm := azuredevops.NoFastForward.String()
twi := new(bool)
*twi = true
completionOpts := azuredevops.GitPullRequestCompletionOptions{
BypassPolicy: new(bool),
BypassReason: azuredevops.String(""),
DeleteSourceBranch: new(bool),
DeleteSourceBranch: &pull.DeleteSourceBranchOnMerge,
MergeCommitMessage: azuredevops.String(common.AutomergeCommitMsg),
MergeStrategy: &mcm,
SquashMerge: new(bool),
Expand All @@ -314,10 +307,23 @@ func (g *AzureDevopsClient) MergePull(pull models.PullRequest) error {

// Construct request body from supplied parameters
mergePull := new(azuredevops.GitPullRequest)
mergePull.AutoCompleteSetBy = &id

mergePull.CompletionOptions = &completionOpts

owner, project, repoName := SplitAzureDevopsRepoFullName(pull.BaseRepo.FullName)

//Before we issue the merge request we need to get the descriptor and uuid of the user who opened this pr
prResp, _, err := g.Client.PullRequests.Get(g.ctx, owner, project, pull.Num, &azuredevops.PullRequestListOptions{})
if err != nil {
return fmt.Errorf("could not merge pull request: %s", err)
}

id := azuredevops.IdentityRef{
Descriptor: prResp.CreatedBy.Descriptor,
ID: prResp.CreatedBy.ID,
ImageURL: prResp.CreatedBy.ImageURL,
}
mergePull.AutoCompleteSetBy = &id
mergeResult, _, err := g.Client.PullRequests.Merge(
g.ctx,
owner,
Expand Down
1 change: 1 addition & 0 deletions server/events/vcs/gitlab_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func (g *GitlabClient) MergePull(pull models.PullRequest) error {
pull.Num,
&gitlab.AcceptMergeRequestOptions{
MergeCommitMessage: &commitMsg,
ShouldRemoveSourceBranch: &pull.DeleteSourceBranchOnMerge,
})
return errors.Wrap(err, "unable to merge merge request, it may not be in a mergeable state")
}
Expand Down
22 changes: 16 additions & 6 deletions server/events/yaml/raw/repo_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ const DefaultParallelApply = false
// DefaultParallelPlan is the default setting for parallel plan
const DefaultParallelPlan = false

// DefaultDeleteSourceBranchOnMerge being false is the default setting whether or not to remove a source branch on merge
const DefaultDeleteSourceBranchOnMerge = false

// RepoCfg is the raw schema for repo-level atlantis.yaml config.
type RepoCfg struct {
Version *int `yaml:"version,omitempty"`
Projects []Project `yaml:"projects,omitempty"`
Workflows map[string]Workflow `yaml:"workflows,omitempty"`
Automerge *bool `yaml:"automerge,omitempty"`
ParallelApply *bool `yaml:"parallel_apply,omitempty"`
ParallelPlan *bool `yaml:"parallel_plan,omitempty"`
Version *int `yaml:"version,omitempty"`
Projects []Project `yaml:"projects,omitempty"`
Workflows map[string]Workflow `yaml:"workflows,omitempty"`
Automerge *bool `yaml:"automerge,omitempty"`
ParallelApply *bool `yaml:"parallel_apply,omitempty"`
ParallelPlan *bool `yaml:"parallel_plan,omitempty"`
DeleteSourceBranchOnMerge *bool `yaml:"delete_source_branch_on_merge,omitempty"`
}

func (r RepoCfg) Validate() error {
Expand Down Expand Up @@ -70,12 +74,18 @@ func (r RepoCfg) ToValid() valid.RepoCfg {
parallelPlan = *r.ParallelPlan
}

deleteBranchOnMerge := DefaultDeleteSourceBranchOnMerge
if r.DeleteSourceBranchOnMerge != nil {
deleteBranchOnMerge = *r.DeleteSourceBranchOnMerge
}

return valid.RepoCfg{
Version: *r.Version,
Projects: validProjects,
Workflows: validWorkflows,
Automerge: automerge,
ParallelApply: parallelApply,
ParallelPlan: parallelPlan,
DeleteSourceBranchOnMerge: deleteBranchOnMerge,
}
}
13 changes: 7 additions & 6 deletions server/events/yaml/valid/repo_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import version "github.com/hashicorp/go-version"
// RepoCfg is the atlantis.yaml config after it's been parsed and validated.
type RepoCfg struct {
// Version is the version of the atlantis YAML file.
Version int
Projects []Project
Workflows map[string]Workflow
Automerge bool
ParallelApply bool
ParallelPlan bool
Version int
Projects []Project
Workflows map[string]Workflow
Automerge bool
ParallelApply bool
ParallelPlan bool
DeleteSourceBranchOnMerge bool
}

func (r RepoCfg) FindProjectsByDirWorkspace(repoRelDir string, workspace string) []Project {
Expand Down
Loading