This is an automated email from the ASF dual-hosted git repository.

abeizn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 0d143121 feat(jenkins): add jenkins pipeline convertor
0d143121 is described below

commit 0d143121bbddda5e028dd747e2361d1f5a359bbe
Author: Yingchu Chen <[email protected]>
AuthorDate: Fri Aug 5 02:23:18 2022 +0800

    feat(jenkins): add jenkins pipeline convertor
---
 models/domainlayer/devops/cicd_pipeline.go         |  10 +-
 models/domainlayer/devops/cicd_task.go             |   2 +-
 plugins/jenkins/impl/impl.go                       |   2 +
 plugins/jenkins/models/build.go                    |   2 +-
 plugins/jenkins/models/build_repo.go               |   2 +-
 .../20220729_modify_all_entities.go                |   7 +-
 plugins/jenkins/models/pipeline.go                 |  27 +++
 .../cicd_task.go => plugins/jenkins/models/task.go |   8 +-
 plugins/jenkins/tasks/build_convertor_cicd.go      | 184 +++++++++++++++++++++
 plugins/jenkins/tasks/build_enricher.go            |  63 +++++++
 10 files changed, 298 insertions(+), 9 deletions(-)

diff --git a/models/domainlayer/devops/cicd_pipeline.go 
b/models/domainlayer/devops/cicd_pipeline.go
index 5550f29c..0b0d266a 100644
--- a/models/domainlayer/devops/cicd_pipeline.go
+++ b/models/domainlayer/devops/cicd_pipeline.go
@@ -16,9 +16,17 @@ type CICDPipeline struct {
        Type         string `gorm:"type:varchar(100);comment: to indicate this 
is CI or CD"`
        DurationSec  uint64
        CreatedDate  time.Time
-       FinishedDate time.Time
+       FinishedDate *time.Time
 }
 
 func (CICDPipeline) TableName() string {
        return "cicd_pipelines"
 }
+
+const (
+       SUCCESS     = "SUCCESS"
+       FAILURE     = "FAILURE"
+       ABORT       = "ABORT"
+       IN_PROGRESS = "IN_PROGRESS"
+       DONE        = "DONE"
+)
diff --git a/models/domainlayer/devops/cicd_task.go 
b/models/domainlayer/devops/cicd_task.go
index 03af484d..bb025b62 100644
--- a/models/domainlayer/devops/cicd_task.go
+++ b/models/domainlayer/devops/cicd_task.go
@@ -14,7 +14,7 @@ type CICDTask struct {
        Type         string `gorm:"type:varchar(100);comment: to indicate this 
is CI or CD"`
        DurationSec  uint64
        StatedDate   time.Time
-       FinishedDate time.Time
+       FinishedDate *time.Time
 }
 
 func (CICDTask) TableName() string {
diff --git a/plugins/jenkins/impl/impl.go b/plugins/jenkins/impl/impl.go
index 167a7355..30036228 100644
--- a/plugins/jenkins/impl/impl.go
+++ b/plugins/jenkins/impl/impl.go
@@ -57,6 +57,8 @@ func (plugin Jenkins) SubTaskMetas() []core.SubTaskMeta {
                tasks.ExtractApiBuildsMeta,
                tasks.CollectApiStagesMeta,
                tasks.ExtractApiStagesMeta,
+               tasks.EnrichApiBuildsMeta,
+               tasks.ConvertBuildsToCICDMeta,
                tasks.ConvertJobsMeta,
                tasks.ConvertBuildsMeta,
        }
diff --git a/plugins/jenkins/models/build.go b/plugins/jenkins/models/build.go
index f3af1a10..fc347bdb 100644
--- a/plugins/jenkins/models/build.go
+++ b/plugins/jenkins/models/build.go
@@ -26,7 +26,6 @@ import (
 // JenkinsBuild db entity for jenkins build
 type JenkinsBuild struct {
        common.NoPKModel
-
        // collected fields
        ConnectionId      uint64    `gorm:"primaryKey"`
        JobName           string    `gorm:"primaryKey;type:varchar(255)"`
@@ -42,6 +41,7 @@ type JenkinsBuild struct {
        Class             string    `gorm:"index;type:varchar(255)" `
        TriggeredBy       string    `gorm:"type:varchar(255)"`
        Building          bool
+       HasStages         bool
 }
 
 func (JenkinsBuild) TableName() string {
diff --git a/plugins/jenkins/models/build_repo.go 
b/plugins/jenkins/models/build_repo.go
index 7f26c54a..104fa3f7 100644
--- a/plugins/jenkins/models/build_repo.go
+++ b/plugins/jenkins/models/build_repo.go
@@ -7,7 +7,7 @@ type JenkinsBuildRepo struct {
        BuildName    string `gorm:"primaryKey;type:varchar(255)"`
        CommitSha    string `gorm:"primaryKey;type:varchar(255)"`
        Branch       string `gorm:"type:varchar(255)"`
-       RepoUrl      string `gorm:"primaryKey;type:varchar(255)"`
+       RepoUrl      string `gorm:"type:varchar(255)"`
        common.NoPKModel
 }
 
diff --git 
a/plugins/jenkins/models/migrationscripts/20220729_modify_all_entities.go 
b/plugins/jenkins/models/migrationscripts/20220729_modify_all_entities.go
index 33a00e19..7ca40e3f 100644
--- a/plugins/jenkins/models/migrationscripts/20220729_modify_all_entities.go
+++ b/plugins/jenkins/models/migrationscripts/20220729_modify_all_entities.go
@@ -53,7 +53,8 @@ type JenkinsBuildRepo0729 struct {
        BuildName    string `gorm:"primaryKey;type:varchar(255)"`
        CommitSha    string `gorm:"primaryKey;type:varchar(255)"`
        Branch       string `gorm:"type:varchar(255)"`
-       RepoUrl      string `gorm:"primaryKey;type:varchar(255)"`
+       RepoUrl      string `gorm:"type:varchar(255)"`
+       HasStages    bool
        archived.NoPKModel
 }
 
@@ -84,6 +85,10 @@ func (*modifyAllEntities) Up(ctx context.Context, db 
*gorm.DB) error {
        if err != nil {
                return err
        }
+       err = db.Migrator().AddColumn(JenkinsBuild0729{}, "class")
+       if err != nil {
+               return err
+       }
        err = db.Migrator().AddColumn(JenkinsBuild0729{}, "triggered_by")
        if err != nil {
                return err
diff --git a/plugins/jenkins/models/pipeline.go 
b/plugins/jenkins/models/pipeline.go
new file mode 100644
index 00000000..4b5b21ad
--- /dev/null
+++ b/plugins/jenkins/models/pipeline.go
@@ -0,0 +1,27 @@
+package models
+
+import (
+       "github.com/apache/incubator-devlake/models/common"
+       "time"
+)
+
+type JenkinsPipeline struct {
+       common.NoPKModel
+       // collected fields
+       ConnectionId uint64 `gorm:"primaryKey"`
+       DurationSec  uint64
+       Name         string    `gorm:"type:varchar(255);primaryKey"`
+       Result       string    // Result
+       Status       string    // Result
+       Timestamp    int64     // start time
+       CreatedDate  time.Time // convered by timestamp
+       CommitSha    string    `gorm:"primaryKey;type:varchar(255)"`
+       Type         string    `gorm:"index;type:varchar(255)"`
+       Building     bool
+       Repo         string `gorm:"type:varchar(255);index"`
+       FinishedDate *time.Time
+}
+
+func (JenkinsPipeline) TableName() string {
+       return "_tool_jenkins_pipelines"
+}
diff --git a/models/domainlayer/devops/cicd_task.go 
b/plugins/jenkins/models/task.go
similarity index 80%
copy from models/domainlayer/devops/cicd_task.go
copy to plugins/jenkins/models/task.go
index 03af484d..4d35b501 100644
--- a/models/domainlayer/devops/cicd_task.go
+++ b/plugins/jenkins/models/task.go
@@ -1,11 +1,11 @@
-package devops
+package models
 
 import (
        "github.com/apache/incubator-devlake/models/domainlayer"
        "time"
 )
 
-type CICDTask struct {
+type JenkinsTask struct {
        domainlayer.DomainEntity
        Name         string `gorm:"type:varchar(255)"`
        PipelineId   string `gorm:"index;type:varchar(255)"`
@@ -17,6 +17,6 @@ type CICDTask struct {
        FinishedDate time.Time
 }
 
-func (CICDTask) TableName() string {
-       return "cicd_tasks"
+func (JenkinsTask) TableName() string {
+       return "_tool_jenkins_tasks"
 }
diff --git a/plugins/jenkins/tasks/build_convertor_cicd.go 
b/plugins/jenkins/tasks/build_convertor_cicd.go
new file mode 100644
index 00000000..a227fdc9
--- /dev/null
+++ b/plugins/jenkins/tasks/build_convertor_cicd.go
@@ -0,0 +1,184 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "fmt"
+       "github.com/apache/incubator-devlake/models/common"
+       "github.com/apache/incubator-devlake/models/domainlayer"
+       "github.com/apache/incubator-devlake/models/domainlayer/devops"
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/core/dal"
+       "github.com/apache/incubator-devlake/plugins/helper"
+       "reflect"
+       "time"
+)
+
+type JenkinsBuildWithRepo struct {
+       // collected fields
+       ConnectionId      uint64    `gorm:"primaryKey"`
+       Duration          float64   // build time
+       DisplayName       string    `gorm:"type:varchar(255)"` // "#7"
+       EstimatedDuration float64   // EstimatedDuration
+       Number            int64     `gorm:"primaryKey"`
+       Result            string    // Result
+       Timestamp         int64     // start time
+       StartTime         time.Time // convered by timestamp
+       CommitSha         string    `gorm:"type:varchar(255)"`
+       Type              string    `gorm:"index;type:varchar(255)"`
+       Class             string    `gorm:"index;type:varchar(255)" `
+       TriggeredBy       string    `gorm:"type:varchar(255)"`
+       Building          bool
+       Branch            string `gorm:"type:varchar(255)"`
+       RepoUrl           string `gorm:"type:varchar(255)"`
+       StageCount        int
+       common.NoPKModel
+}
+
+var ConvertBuildsToCICDMeta = core.SubTaskMeta{
+       Name:             "convertBuildsToCICD",
+       EntryPoint:       ConvertBuildsToCICD,
+       EnabledByDefault: true,
+       Description:      "convert builds to cicd",
+       DomainTypes:      []string{core.DOMAIN_TYPE_CICD},
+}
+
+func ConvertBuildsToCICD(taskCtx core.SubTaskContext) error {
+       db := taskCtx.GetDal()
+       data := taskCtx.GetData().(*JenkinsTaskData)
+
+       clauses := []dal.Clause{
+               dal.Select(`tjb.connection_id, tjb.duration, tjb.display_name, 
tjb.estimated_duration, tjb.number,
+                       tjb.result, tjb.timestamp, tjb.start_time, 
tjbr.commit_sha, tjb.type, tjb.class, 
+                       tjb.triggered_by, tjb.building, tjbr.branch, 
tjbr.repo_url`),
+               dal.From("_tool_jenkins_builds tjb"),
+               dal.Join("left join _tool_jenkins_build_repos tjbr on 
tjbr.build_name = tjb.display_name"),
+               dal.Where("tjb.connection_id = ?", data.Options.ConnectionId),
+       }
+       cursor, err := db.Cursor(clauses...)
+       if err != nil {
+               return err
+       }
+       defer cursor.Close()
+
+       //jobIdGen := didgen.NewDomainIdGenerator(&models.JenkinsJob{})
+       //buildIdGen := didgen.NewDomainIdGenerator(&models.JenkinsBuild{})
+
+       converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+               InputRowType: reflect.TypeOf(JenkinsBuildWithRepo{}),
+               Input:        cursor,
+               RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
+                       Params: JenkinsApiParams{
+                               ConnectionId: data.Options.ConnectionId,
+                       },
+                       Ctx:   taskCtx,
+                       Table: RAW_BUILD_TABLE,
+               },
+               Convert: func(inputRow interface{}) ([]interface{}, error) {
+                       jenkinsBuildWithRepo := inputRow.(*JenkinsBuildWithRepo)
+                       durationSec := int64(jenkinsBuildWithRepo.Duration / 
1000)
+                       jenkinsPipelineResult := ""
+                       jenkinsPipelineStatus := ""
+                       var jenkinsPipelineFinishedDate *time.Time
+                       results := make([]interface{}, 0)
+                       if jenkinsBuildWithRepo.Result == "SUCCESS" {
+                               jenkinsPipelineResult = devops.SUCCESS
+                       } else if jenkinsBuildWithRepo.Result == "FAILURE" {
+                               jenkinsPipelineResult = devops.FAILURE
+                       } else {
+                               jenkinsPipelineResult = devops.ABORT
+                       }
+
+                       if jenkinsBuildWithRepo.Building {
+                               jenkinsPipelineStatus = devops.IN_PROGRESS
+                       } else {
+                               jenkinsPipelineStatus = devops.DONE
+                               finishTime := 
jenkinsBuildWithRepo.StartTime.Add(time.Duration(durationSec * 
int64(time.Second)))
+                               jenkinsPipelineFinishedDate = &finishTime
+                       }
+                       if jenkinsBuildWithRepo.TriggeredBy == "" {
+                               jenkinsPipeline := &devops.CICDPipeline{
+                                       DomainEntity: domainlayer.DomainEntity{
+                                               Id: 
fmt.Sprintf("%s:%s:%d:%s:%s", "jenkins", "JenkinsPipeline", 
jenkinsBuildWithRepo.ConnectionId,
+                                                       
jenkinsBuildWithRepo.CommitSha, jenkinsBuildWithRepo.DisplayName),
+                                       },
+                                       Name:         
jenkinsBuildWithRepo.DisplayName,
+                                       CommitSha:    
jenkinsBuildWithRepo.CommitSha,
+                                       Branch:       
jenkinsBuildWithRepo.Branch,
+                                       Repo:         
jenkinsBuildWithRepo.RepoUrl,
+                                       Result:       jenkinsPipelineResult,
+                                       Status:       jenkinsPipelineStatus,
+                                       FinishedDate: 
jenkinsPipelineFinishedDate,
+                                       //Type:         "",
+                                       DurationSec: uint64(durationSec),
+                                       CreatedDate: 
jenkinsBuildWithRepo.StartTime,
+                               }
+                               if jenkinsPipelineFinishedDate != nil {
+                               }
+                               results = append(results, jenkinsPipeline)
+                       }
+
+                       if jenkinsBuildWithRepo.StageCount == 0 {
+                               jenkinsTask := &devops.CICDTask{
+                                       DomainEntity: domainlayer.DomainEntity{
+                                               Id: fmt.Sprintf("%s:%s:%d:%s", 
"jenkins", "JenkinsTask", jenkinsBuildWithRepo.ConnectionId,
+                                                       
jenkinsBuildWithRepo.DisplayName),
+                                       },
+                                       Name:   
jenkinsBuildWithRepo.DisplayName,
+                                       Result: jenkinsPipelineResult,
+                                       Status: jenkinsPipelineStatus,
+                                       //Type:         "",
+                                       DurationSec:  uint64(durationSec),
+                                       StatedDate:   
jenkinsBuildWithRepo.StartTime,
+                                       FinishedDate: 
jenkinsPipelineFinishedDate,
+                               }
+                               if jenkinsBuildWithRepo.TriggeredBy != "" {
+                                       tmp := make([]*JenkinsBuildWithRepo, 0)
+                                       clauses := []dal.Clause{
+                                               dal.Select(`tjb.connection_id, 
tjb.duration, tjb.display_name, tjb.estimated_duration, tjb.number,
+                                                       tjb.result, 
tjb.timestamp, tjbr.commit_sha, tjb.type, tjb.class, 
+                                                       tjb.triggered_by, 
tjb.building, tjbr.branch, tjbr.repo_url`),
+                                               dal.From("_tool_jenkins_builds 
tjb"),
+                                               dal.Join("left join 
_tool_jenkins_build_repos tjbr on tjbr.build_name = tjb.display_name"),
+                                               dal.Where("tjb.connection_id = 
? and tjb.display_name = ?", data.Options.ConnectionId, 
jenkinsBuildWithRepo.TriggeredBy),
+                                       }
+                                       err = db.All(&tmp, clauses...)
+                                       if err != nil {
+                                               return nil, err
+                                       }
+                                       if len(tmp) > 0 {
+                                               jenkinsTask.PipelineId = 
fmt.Sprintf("%s:%s:%d:%s:%s", "jenkins", "JenkinsPipeline", 
jenkinsBuildWithRepo.ConnectionId,
+                                                       tmp[0].CommitSha, 
tmp[0].DisplayName)
+                                       }
+                               } else {
+                                       jenkinsTask.PipelineId = 
fmt.Sprintf("%s:%s:%d:%s:%s", "jenkins", "JenkinsPipeline", 
jenkinsBuildWithRepo.ConnectionId,
+                                               jenkinsBuildWithRepo.CommitSha, 
jenkinsBuildWithRepo.DisplayName)
+                               }
+                               results = append(results, jenkinsTask)
+
+                       }
+
+                       return results, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return converter.Execute()
+}
diff --git a/plugins/jenkins/tasks/build_enricher.go 
b/plugins/jenkins/tasks/build_enricher.go
new file mode 100644
index 00000000..ceed2726
--- /dev/null
+++ b/plugins/jenkins/tasks/build_enricher.go
@@ -0,0 +1,63 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "github.com/apache/incubator-devlake/plugins/core"
+       "github.com/apache/incubator-devlake/plugins/core/dal"
+       "github.com/apache/incubator-devlake/plugins/jenkins/models"
+)
+
+// this struct should be moved to `gitub_api_common.go`
+
+var EnrichApiBuildsMeta = core.SubTaskMeta{
+       Name:             "enrichApiBuilds",
+       EntryPoint:       EnrichApiBuilds,
+       EnabledByDefault: true,
+       Description:      "Enrich  jenkins_builds",
+       DomainTypes:      []string{core.DOMAIN_TYPE_CICD},
+}
+
+func EnrichApiBuilds(taskCtx core.SubTaskContext) error {
+       data := taskCtx.GetData().(*JenkinsTaskData)
+       db := taskCtx.GetDal()
+       clauses := []dal.Clause{
+               dal.Select("distinct build_name"),
+               dal.From(&models.JenkinsStage{}),
+               dal.Where("connection_id = ?", data.Options.ConnectionId),
+               dal.Groupby("build_name"),
+       }
+       cursor, err := db.Cursor(clauses...)
+       for cursor.Next() {
+               var buildName string
+               err = cursor.Scan(&buildName)
+               if err != nil {
+                       return err
+               }
+               if buildName == "" {
+                       continue
+               }
+               build := &models.JenkinsBuild{}
+               build.HasStages = true
+               err = db.Update(&models.JenkinsBuild{}, 
dal.Where("connection_id = ? and build_name = ?", data.Options.ConnectionId, 
buildName))
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}

Reply via email to