Skip to content

Commit

Permalink
fix(api): copy git params from parents (#4169)
Browse files Browse the repository at this point in the history
  • Loading branch information
sguiheux authored Apr 9, 2019
1 parent e24860d commit 173f46f
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 58 deletions.
12 changes: 9 additions & 3 deletions engine/api/workflow/execute_node_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -986,9 +986,15 @@ func getVCSInfos(ctx context.Context, db gorp.SqlExecutor, store cache.Store, vc
vcsInfos.Hash = defaultB.LatestCommit
case vcsInfos.Hash == "" && vcsInfos.Branch != "":
// GET COMMIT INFO
branch, err := client.Branch(ctx, vcsInfos.Repository, vcsInfos.Branch)
if err != nil {
return nil, sdk.NewError(sdk.ErrBranchNameNotProvided, err)
branch, errB := client.Branch(ctx, vcsInfos.Repository, vcsInfos.Branch)
if errB != nil {
// Try default branch
b, errD := repositoriesmanager.DefaultBranch(ctx, client, vcsInfos.Repository)
if errD != nil {
return nil, errD
}
branch = &b
vcsInfos.Branch = branch.DisplayID
}
vcsInfos.Hash = branch.LatestCommit
}
Expand Down
137 changes: 85 additions & 52 deletions engine/api/workflow/process_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func processNode(ctx context.Context, db gorp.SqlExecutor, store cache.Store, pr
// NODE CONTEXT BUILD PARAMETER
computeNodeContextBuildParameters(ctx, proj, wr, run, n, runContext)

// PARENT BUILD PARAMETER
// PARENT BUILD PARAMETER WITH git.*
if len(parents) > 0 {
_, next := observability.Span(ctx, "workflow.getParentParameters")
parentsParams, errPP := getParentParameters(wr, parents)
Expand All @@ -164,69 +164,88 @@ func processNode(ctx context.Context, db gorp.SqlExecutor, store cache.Store, pr
run.BuildParameters = sdk.ParametersFromMap(sdk.ParametersMapMerge(mapBuildParams, mapParentParams))
}

currentApplicationParam := sdk.ParameterFind(&run.BuildParameters, "cds.application")
isRoot := n.ID == wr.Workflow.WorkflowData.Node.ID

// GIT PARAMS
// Here, run.BuildParameters contains parent git params, get from getParentParameters

// GIT PARAMETER VALUE
var app sdk.Application
var currentRepo string
currentJobGitValues := map[string]string{}
for _, param := range run.BuildParameters {
switch param.Name {
case tagGitHash, tagGitBranch, tagGitTag, tagGitAuthor, tagGitMessage, tagGitRepository, tagGitURL, tagGitHTTPURL:
currentJobGitValues[param.Name] = param.Value
}
needVCSInfo := false

// Get current application and repository
if n.Context.ApplicationID != 0 {
app = wr.Workflow.Applications[n.Context.ApplicationID]
currentRepo = app.RepositoryFullname
}

// Retrieve previous git information
if currentApplicationParam != nil && n.Context.ApplicationID > 0 && wr.Workflow.Applications[n.Context.ApplicationID].RepositoryFullname != "" {
for _, nr := range wr.WorkflowNodeRuns {
nrApplicationParam := sdk.ParameterFind(&nr[0].BuildParameters, "cds.application")
if nrApplicationParam == nil {
continue
}
p := sdk.ParameterFind(&nr[0].BuildParameters, tagGitRepository)
if p == nil || (currentApplicationParam.Value != nrApplicationParam.Value && p.Value != wr.Workflow.Applications[n.Context.ApplicationID].RepositoryFullname) {
continue
}
parentRepo := sdk.ParameterFind(&run.BuildParameters, tagGitRepository)

// retrieve git information
for _, tag := range []string{tagGitHash, tagGitBranch, tagGitTag, tagGitAuthor, tagGitMessage, tagGitRepository, tagGitURL, tagGitHTTPURL} {
p := sdk.ParameterFind(&nr[0].BuildParameters, tag)
if p != nil {
currentJobGitValues[tag] = p.Value
}
// Compute git params for current job
// Get from parent when
// * is root because they come from payload
// * no repo on current job
// * parent was on same repo
if isRoot || currentRepo == "" || (parentRepo != nil && parentRepo.Value == currentRepo) {
for _, param := range run.BuildParameters {
switch param.Name {
case tagGitHash, tagGitBranch, tagGitTag, tagGitAuthor, tagGitMessage, tagGitRepository, tagGitURL, tagGitHTTPURL:
currentJobGitValues[param.Name] = param.Value
}
}
} else {
// GET GIT INFO
for _, pa := range parents {
// retrieve git information
for _, tag := range []string{tagGitHash, tagGitBranch, tagGitTag, tagGitAuthor, tagGitMessage, tagGitRepository, tagGitURL, tagGitHTTPURL} {
p := sdk.ParameterFind(&pa.BuildParameters, tag)
if p != nil {
currentJobGitValues[tag] = p.Value
if isRoot {
needVCSInfo = true
}
}
// Find an ancestor on the same repo
if currentRepo != "" && parentRepo != nil && parentRepo.Value != currentRepo {
// Try to found a parent on the same repo
found := false
for _, parent := range wr.WorkflowNodeRuns {
repo := sdk.ParameterFind(&parent[0].BuildParameters, tagGitRepository)
if repo != nil && repo.Value == currentRepo {
found = true
// copy git info from ancestors
for _, param := range parent[0].BuildParameters {
switch param.Name {
case tagGitHash, tagGitBranch, tagGitTag, tagGitAuthor, tagGitMessage, tagGitRepository, tagGitURL, tagGitHTTPURL:
currentJobGitValues[param.Name] = param.Value
}
}
break
}
}
// If we change repo and we dont find ancestor on the same repo, just keep the branch
if !found {
b := sdk.ParameterFind(&run.BuildParameters, tagGitBranch)
if b != nil {
currentJobGitValues[tagGitBranch] = b.Value
}
needVCSInfo = true
}
}

isRoot := n.ID == wr.Workflow.WorkflowData.Node.ID
var vcsInfos *vcsInfos
var app sdk.Application
if n.Context.ApplicationID != 0 {
app = wr.Workflow.Applications[n.Context.ApplicationID]
}
// GET VCS Infos IF NEEDED
// * root Node
// * different repo
var vcsInf *vcsInfos
var errVcs error
vcsServer := repositoriesmanager.GetProjectVCSServer(proj, app.VCSServer)
vcsInfos, errVcs = getVCSInfos(ctx, db, store, vcsServer, currentJobGitValues, app.Name, app.VCSServer, app.RepositoryFullname)
if errVcs != nil {
AddWorkflowRunInfo(wr, true, sdk.SpawnMsg{
ID: sdk.MsgWorkflowError.ID,
Args: []interface{}{errVcs.Error()},
})
return nil, false, sdk.WrapError(errVcs, "unable to get git informations")
if needVCSInfo {
vcsServer := repositoriesmanager.GetProjectVCSServer(proj, app.VCSServer)
vcsInf, errVcs = getVCSInfos(ctx, db, store, vcsServer, currentJobGitValues, app.Name, app.VCSServer, app.RepositoryFullname)
if errVcs != nil {
AddWorkflowRunInfo(wr, true, sdk.SpawnMsg{
ID: sdk.MsgWorkflowError.ID,
Args: []interface{}{errVcs.Error()},
})
return nil, false, sdk.WrapError(errVcs, "unable to get git informations")
}
}

if isRoot && vcsInfos != nil {
setValuesGitInBuildParameters(run, *vcsInfos)
// Update git params / git columns
if isRoot && vcsInf != nil {
setValuesGitInBuildParameters(run, *vcsInf)
}

// CONDITION
Expand Down Expand Up @@ -256,8 +275,22 @@ func processNode(ctx context.Context, db gorp.SqlExecutor, store cache.Store, pr
}
}

if !isRoot && vcsInfos != nil {
setValuesGitInBuildParameters(run, *vcsInfos)
// Resync vcsInfos if we dont call func getVCSInfos
if !needVCSInfo {
vcsInf = &vcsInfos{}
vcsInf.Repository = currentJobGitValues[tagGitRepository]
vcsInf.Branch = currentJobGitValues[tagGitBranch]
vcsInf.Tag = currentJobGitValues[tagGitTag]
vcsInf.Hash = currentJobGitValues[tagGitHash]
vcsInf.Author = currentJobGitValues[tagGitAuthor]
vcsInf.Message = currentJobGitValues[tagGitMessage]
vcsInf.URL = currentJobGitValues[tagGitURL]
vcsInf.HTTPUrl = currentJobGitValues[tagGitHTTPURL]
}

// Update datas if repo change
if !isRoot && vcsInf != nil {
setValuesGitInBuildParameters(run, *vcsInf)
}

// ADD TAG
Expand All @@ -275,7 +308,7 @@ func processNode(ctx context.Context, db gorp.SqlExecutor, store cache.Store, pr
} else {
wr.Tag(tagGitHash, run.VCSHash)
}
wr.Tag(tagGitAuthor, vcsInfos.Author)
wr.Tag(tagGitAuthor, vcsInf.Author)
}

// Add env tag
Expand Down
173 changes: 173 additions & 0 deletions engine/api/workflow/process_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,20 @@ func TestManualRunBuildParameterMultiApplication(t *testing.T) {
if err := enc.Encode(bs); err != nil {
return writeError(w, err)
}
case "/vcs/stash/repos/ovh/cds/branches/?branch=feat%2Fbranch":
return writeError(w, sdk.ErrNotFound)
case "/vcs/github/repos/sguiheux/demo/branches":
bs := []sdk.VCSBranch{
{
LatestCommit: "defaultCommit",
DisplayID: "defaultBranch",
Default: true,
ID: "1",
},
}
if err := enc.Encode(bs); err != nil {
return writeError(w, err)
}
case "/vcs/stash/repos/ovh/cds/commits/defaultCommit":
c := sdk.VCSCommit{
Author: sdk.VCSAuthor{
Expand Down Expand Up @@ -1332,6 +1346,165 @@ func TestGitParamOn2ApplicationSameRepo(t *testing.T) {

}

// Payload: branch only + run condition on git.branch
func TestManualRunWithPayloadAndRunCondition(t *testing.T) {
db, cache, end := test.SetupPG(t, bootstrap.InitiliazeDB)
defer end()
u, _ := assets.InsertAdminUser(db)

// Create project
key := sdk.RandomString(10)
proj := assets.InsertTestProject(t, db, cache, key, key, u)
assert.NoError(t, repositoriesmanager.InsertForProject(db, proj, &sdk.ProjectVCSServer{
Name: "github",
Data: map[string]string{
"token": "foo",
"secret": "bar",
},
}))

mockVCSSservice := &sdk.Service{Name: "TestManualRunWithPayloadProcessNodeBuildParameter", Type: services.TypeVCS}
test.NoError(t, services.Insert(db, mockVCSSservice))
defer func() {
_ = services.Delete(db, mockVCSSservice) // nolint
}()

//This is a mock for the vcs service
services.HTTPClient = mock(
func(r *http.Request) (*http.Response, error) {
body := new(bytes.Buffer)
w := new(http.Response)
enc := json.NewEncoder(body)
w.Body = ioutil.NopCloser(body)

switch r.URL.String() {
// NEED get REPO
case "/vcs/github/repos/sguiheux/demo":
repo := sdk.VCSRepo{
URL: "https",
Name: "demo",
ID: "123",
Fullname: "sguiheux/demo",
Slug: "sguiheux",
HTTPCloneURL: "https://github.com/sguiheux/demo.git",
SSHCloneURL: "git://github.com/sguiheux/demo.git",
}
if err := enc.Encode(repo); err != nil {
return writeError(w, err)
}
// NEED GET BRANCH TO GET LASTEST COMMIT
case "/vcs/github/repos/sguiheux/demo/branches/?branch=feat%2Fbranch":
b := sdk.VCSBranch{
Default: false,
DisplayID: "feat/branch",
LatestCommit: "mylastcommit",
}
if err := enc.Encode(b); err != nil {
return writeError(w, err)
}
// NEED GET COMMIT TO GET AUTHOR AND MESSAGE
case "/vcs/github/repos/sguiheux/demo/commits/mylastcommit":
c := sdk.VCSCommit{
Author: sdk.VCSAuthor{
Name: "steven.guiheux",
Email: "[email protected]",
},
Hash: "mylastcommit",
Message: "super commit",
Timestamp: time.Now().Unix(),
}
if err := enc.Encode(c); err != nil {
return writeError(w, err)
}
default:
t.Fatalf("UNKNOWN ROUTE: %s", r.URL.String())
}

return w, nil
},
)

pip := createEmptyPipeline(t, db, cache, proj, u)
//pip2 := createBuildPipeline(t, db, cache, proj, u)
app := createApplication1(t, db, cache, proj, u)

// RELOAD PROJECT WITH DEPENDENCIES
proj.Applications = append(proj.Applications, *app)
proj.Pipelines = append(proj.Pipelines, *pip)

// WORKFLOW TO RUN
w := sdk.Workflow{
ProjectID: proj.ID,
ProjectKey: proj.Key,
Name: sdk.RandomString(10),
WorkflowData: &sdk.WorkflowData{
Node: sdk.Node{
Name: "root",
Type: sdk.NodeTypePipeline,
Context: &sdk.NodeContext{
PipelineID: proj.Pipelines[0].ID,
ApplicationID: proj.Applications[0].ID,
},
Triggers: []sdk.NodeTrigger{
{
ChildNode: sdk.Node{
Name: "child",
Type: sdk.NodeTypePipeline,
Context: &sdk.NodeContext{
PipelineID: proj.Pipelines[0].ID,
ApplicationID: proj.Applications[0].ID,
Conditions: sdk.WorkflowNodeConditions{
PlainConditions: []sdk.WorkflowNodeCondition{
{
Variable: "git.branch",
Operator: "eq",
Value: "feat/branch",
},
},
},
},
},
},
},
},
},
Applications: map[int64]sdk.Application{
proj.Applications[0].ID: proj.Applications[0],
},
Pipelines: map[int64]sdk.Pipeline{
proj.Pipelines[0].ID: proj.Pipelines[0],
},
}

(&w).RetroMigrate()
assert.NoError(t, workflow.Insert(db, cache, &w, proj, u))

// CREATE RUN
var manualEvent sdk.WorkflowNodeRunManual
manualEvent.Payload = map[string]string{
"git.branch": "feat/branch",
}

opts := &sdk.WorkflowRunPostHandlerOption{
Manual: &manualEvent,
}
wr, err := workflow.CreateRun(db, &w, opts, u)
assert.NoError(t, err)
wr.Workflow = w

_, errR := workflow.StartWorkflowRun(context.TODO(), db, cache, proj, wr, opts, u, nil)
assert.NoError(t, errR)

assert.Equal(t, 2, len(wr.WorkflowNodeRuns))
assert.Equal(t, 1, len(wr.WorkflowNodeRuns[w.WorkflowData.Node.Triggers[0].ChildNode.ID]))

mapParams := sdk.ParametersToMap(wr.WorkflowNodeRuns[w.WorkflowData.Node.Triggers[0].ChildNode.ID][0].BuildParameters)
assert.Equal(t, "feat/branch", mapParams["git.branch"])
assert.Equal(t, "mylastcommit", mapParams["git.hash"])
assert.Equal(t, "steven.guiheux", mapParams["git.author"])
assert.Equal(t, "super commit", mapParams["git.message"])
}

func createEmptyPipeline(t *testing.T, db gorp.SqlExecutor, cache cache.Store, proj *sdk.Project, u *sdk.User) *sdk.Pipeline {
pip := &sdk.Pipeline{
Name: "build",
Expand Down
Loading

0 comments on commit 173f46f

Please sign in to comment.