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

markusb pushed a commit to branch feat/ado-disabled-repos
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git

commit 15a0545b469820c05a0d76bd0dc185ee172cbc3f
Author: Markus Braunbeck <[email protected]>
AuthorDate: Sun Jun 23 14:02:18 2024 +0200

    feat(azuredevops): add support for disabled Azure DevOps repos
---
 .../plugins/azuredevops_go/api/blueprint_v200.go   | 135 ++++++++++---------
 .../azuredevops_go/api/blueprint_v200_test.go      | 150 ++++++++++++++++++++-
 backend/plugins/azuredevops_go/api/init.go         |   4 +-
 .../plugins/azuredevops_go/api/remote_helper.go    |  13 +-
 4 files changed, 229 insertions(+), 73 deletions(-)

diff --git a/backend/plugins/azuredevops_go/api/blueprint_v200.go 
b/backend/plugins/azuredevops_go/api/blueprint_v200.go
index a0fea7695..5f3ee38a8 100644
--- a/backend/plugins/azuredevops_go/api/blueprint_v200.go
+++ b/backend/plugins/azuredevops_go/api/blueprint_v200.go
@@ -20,8 +20,6 @@ package api
 import (
        "net/url"
 
-       "golang.org/x/exp/slices"
-
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/utils"
 
@@ -71,51 +69,39 @@ func makeScopeV200(
        sc := make([]plugin.Scope, 0, 3*len(scopeDetails))
 
        for _, scope := range scopeDetails {
-               azuredevopsRepo, scopeConfig := scope.Scope, scope.ScopeConfig
-               if azuredevopsRepo.Type != models.RepositoryTypeADO {
+               repo, scopeConfig := scope.Scope, scope.ScopeConfig
+
+               if len(scopeConfig.Entities) == 0 {
+                       logger.Printf("Precondition failed. Found empty 
ScopeConfig for Scope: %v. Skipping", repo.Name)
                        continue
                }
-               id := 
didgen.NewDomainIdGenerator(&models.AzuredevopsRepo{}).Generate(connectionId, 
azuredevopsRepo.Id)
 
-               if !azuredevopsRepo.IsDisabled && 
(utils.StringsContains(scopeConfig.Entities, plugin.DOMAIN_TYPE_CODE_REVIEW) ||
-                       utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CODE)) {
-                       scopeRepo := code.NewRepo(id, azuredevopsRepo.Name)
+               isDomainCode := utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CODE_REVIEW) ||
+                       utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CODE)
+               isDomainCICD := utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CICD)
+               isDomainTicket := utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_TICKET)
+
+               id := 
didgen.NewDomainIdGenerator(&models.AzuredevopsRepo{}).Generate(connectionId, 
repo.Id)
+
+               // DOMAIN_TYPE_CODE (i.e. gitextractor, rediff) only works if 
the repository is public and not disabled
+               if isDomainCode && !repo.IsDisabled && !repo.IsPrivate {
+                       scopeRepo := code.NewRepo(id, repo.Name)
                        sc = append(sc, scopeRepo)
                }
 
                // add cicd_scope to scopes
-               if utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CICD) {
-                       scopeCICD := devops.NewCicdScope(id, 
azuredevopsRepo.Name)
+               if isDomainCICD {
+                       scopeCICD := devops.NewCicdScope(id, repo.Name)
                        sc = append(sc, scopeCICD)
                }
 
                // add board to scopes
-               if utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_TICKET) {
-                       scopeTicket := ticket.NewBoard(id, azuredevopsRepo.Name)
+               if isDomainTicket {
+                       scopeTicket := ticket.NewBoard(id, repo.Name)
                        sc = append(sc, scopeTicket)
                }
        }
 
-       for _, scope := range scopeDetails {
-               azuredevopsRepo, scopeConfig := scope.Scope, scope.ScopeConfig
-               if azuredevopsRepo.Type == models.RepositoryTypeADO {
-                       continue
-               }
-               id := 
didgen.NewDomainIdGenerator(&models.AzuredevopsRepo{}).Generate(connectionId, 
azuredevopsRepo.Id)
-
-               // Azure DevOps Pipeline can be used with remote repositories 
such as GitHub and Bitbucket
-               if utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CICD) {
-                       scopeCICD := devops.NewCicdScope(id, 
azuredevopsRepo.Name)
-                       sc = append(sc, scopeCICD)
-               }
-
-               // DOMAIN_TYPE_CODE (i.e. gitextractor, rediff) only works if 
the repository is public
-               if !azuredevopsRepo.IsPrivate && 
utils.StringsContains(scopeConfig.Entities, plugin.DOMAIN_TYPE_CODE) {
-                       scopeRepo := code.NewRepo(id, azuredevopsRepo.Name)
-                       sc = append(sc, scopeRepo)
-               }
-       }
-
        return sc, nil
 }
 
@@ -125,62 +111,91 @@ func makePipelinePlanV200(
        scopeDetails []*srvhelper.ScopeDetail[models.AzuredevopsRepo, 
models.AzuredevopsScopeConfig],
 ) (coreModels.PipelinePlan, errors.Error) {
        plans := make(coreModels.PipelinePlan, 0, 3*len(scopeDetails))
+
        for _, scope := range scopeDetails {
-               azuredevopsRepo, scopeConfig := scope.Scope, scope.ScopeConfig
-               var stage coreModels.PipelineStage
-               var err errors.Error
+               repo, scopeConfig := scope.Scope, scope.ScopeConfig
+
+               if len(scopeConfig.Entities) == 0 {
+                       logger.Printf("Precondition failed. Found empty 
ScopeConfig for Scope: %v. Skipping",
+                               repo.Name)
+                       continue
+               }
 
                options := make(map[string]interface{})
-               options["name"] = azuredevopsRepo.Name // this is solely for 
the FE to display the repo name of a task
+               options["name"] = repo.Name // this is solely for the FE to 
display the repo name of a task
 
                options["connectionId"] = connection.ID
-               options["organizationId"] = azuredevopsRepo.OrganizationId
-               options["projectId"] = azuredevopsRepo.ProjectId
-               options["externalId"] = azuredevopsRepo.ExternalId
-               options["repositoryId"] = azuredevopsRepo.Id
-               options["repositoryType"] = azuredevopsRepo.Type
+               options["organizationId"] = repo.OrganizationId
+               options["projectId"] = repo.ProjectId
+               options["externalId"] = repo.ExternalId
+               options["repositoryId"] = repo.Id
+               options["repositoryType"] = repo.Type
 
                // construct subtasks
                var entities []string
-               if scope.Scope.Type == models.RepositoryTypeADO {
-                       entities = append(entities, scopeConfig.Entities...)
-               } else {
-                       if i := slices.Index(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CICD); i >= 0 {
-                               entities = append(entities, 
scopeConfig.Entities[i])
-                       }
+               var blockedEntities []string
+
+               // We are unable to check out the code or gather pull requests 
for repositories that are disabled (DevOps)
+               // or private (GitHub)
+               if repo.IsDisabled || repo.IsPrivate {
+                       blockedEntities = append(blockedEntities, []string{
+                               plugin.DOMAIN_TYPE_CODE,
+                               plugin.DOMAIN_TYPE_CODE_REVIEW,
+                       }...)
+               }
 
-                       if i := slices.Index(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CODE); i >= 0 && !scope.Scope.IsPrivate {
-                               entities = append(entities, 
scopeConfig.Entities[i])
+               // We are unable to gather pull requests from repositories not 
hosted on DevOps.
+               // However, we can still check out the code if the repository 
is publicly available
+               if repo.Type != models.RepositoryTypeADO {
+                       blockedEntities = append(blockedEntities, []string{
+                               plugin.DOMAIN_TYPE_CODE_REVIEW,
+                       }...)
+               }
+
+               for _, v := range scopeConfig.Entities {
+                       if !utils.StringsContains(blockedEntities, v) {
+                               entities = append(entities, v)
                        }
                }
 
-               subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, 
entities)
+               var subtasks []string
+               var err errors.Error
+               if len(entities) > 0 {
+                       // if entities is empty MakePipelinePlanSubtasks 
assumes that we want to
+                       // enable all entity types
+                       subtasks, err = 
helper.MakePipelinePlanSubtasks(subtaskMetas, entities)
+               }
                if err != nil {
                        return nil, err
                }
 
-               stage = append(stage, &coreModels.PipelineTask{
-                       Plugin:   "azuredevops_go",
-                       Subtasks: subtasks,
-                       Options:  options,
-               })
+               var stage []*coreModels.PipelineTask
+               if len(subtasks) > 0 {
+                       stage = append(stage, &coreModels.PipelineTask{
+                               Plugin:   "azuredevops_go",
+                               Subtasks: subtasks,
+                               Options:  options,
+                       })
+               } else {
+                       logger.Printf("Skipping azuredevops_go plugin due to 
empty subtasks. Please check your scope config")
+               }
 
                // collect git data by gitextractor if CODE was requested
-               if utils.StringsContains(scopeConfig.Entities, 
plugin.DOMAIN_TYPE_CODE) && !scope.Scope.IsPrivate || len(scopeConfig.Entities) 
== 0 {
-                       cloneUrl, err := 
errors.Convert01(url.Parse(azuredevopsRepo.RemoteUrl))
+               if !repo.IsPrivate && !repo.IsDisabled && 
utils.StringsContains(scopeConfig.Entities, plugin.DOMAIN_TYPE_CODE) {
+                       cloneUrl, err := 
errors.Convert01(url.Parse(repo.RemoteUrl))
                        if err != nil {
                                return nil, err
                        }
 
-                       if scope.Scope.Type == models.RepositoryTypeADO {
+                       if repo.Type == models.RepositoryTypeADO {
                                cloneUrl.User = url.UserPassword("git", 
connection.Token)
                        }
                        stage = append(stage, &coreModels.PipelineTask{
                                Plugin: "gitextractor",
                                Options: map[string]interface{}{
                                        "url":            cloneUrl.String(),
-                                       "name":           azuredevopsRepo.Name,
-                                       "repoId":         
didgen.NewDomainIdGenerator(&models.AzuredevopsRepo{}).Generate(connection.ID, 
azuredevopsRepo.Id),
+                                       "name":           repo.Name,
+                                       "repoId":         
didgen.NewDomainIdGenerator(&models.AzuredevopsRepo{}).Generate(connection.ID, 
repo.Id),
                                        "proxy":          connection.Proxy,
                                        "noShallowClone": true,
                                },
diff --git a/backend/plugins/azuredevops_go/api/blueprint_v200_test.go 
b/backend/plugins/azuredevops_go/api/blueprint_v200_test.go
index 2cb2a2513..d733689a9 100644
--- a/backend/plugins/azuredevops_go/api/blueprint_v200_test.go
+++ b/backend/plugins/azuredevops_go/api/blueprint_v200_test.go
@@ -19,6 +19,8 @@ package api
 
 import (
        "fmt"
+       
"github.com/apache/incubator-devlake/helpers/pluginhelper/subtaskmeta/sorter"
+       "github.com/apache/incubator-devlake/impls/logruslog"
        "reflect"
        "strings"
        "testing"
@@ -38,6 +40,7 @@ import (
 const (
        connectionID        uint64 = 1
        azuredevopsRepoId          = "ad05901f-c9b0-4938-bc8a-a22eb2467ceb"
+       azureDevOpsToken           = "ado-pat"
        expectDomainScopeId        = 
"azuredevops_go:AzuredevopsRepo:1:ad05901f-c9b0-4938-bc8a-a22eb2467ceb"
 )
 
@@ -47,6 +50,12 @@ func mockAzuredevopsPlugin(t *testing.T) {
        mockMeta.On("Name").Return("dummy").Maybe()
        err := plugin.RegisterPlugin("azuredevops_go", mockMeta)
        assert.Equal(t, err, nil)
+
+       // The logger is assigned within the Init function, which is not 
executed during unit tests.
+       // To avoid a nil pointer, we need to manually set it here.
+       if logger == nil {
+               logger = logruslog.Global
+       }
 }
 
 func TestMakeScopes(t *testing.T) {
@@ -191,12 +200,27 @@ func TestMakeRemoteRepoScopes(t *testing.T) {
                Type           string
                Private        bool
                Disabled       bool
+               Entities       []string // Data Entities configured in a scope 
config
                ExpectedScopes []string
        }{
-               {Name: "Azure DevOps Repository", Type: 
models.RepositoryTypeADO, Private: false, ExpectedScopes: 
[]string{"*code.Repo", "*ticket.Board", "*devops.CicdScope"}},
-               {Name: "Azure DevOps disabled Repository", Type: 
models.RepositoryTypeADO, Disabled: true, ExpectedScopes: 
[]string{"*ticket.Board", "*devops.CicdScope"}},
-               {Name: "Public GitHub Repository", Type: 
models.RepositoryTypeGithub, Private: false, ExpectedScopes: 
[]string{"*code.Repo", "*devops.CicdScope"}},
-               {Name: "Private GitHub Repository", Type: 
models.RepositoryTypeGithub, Private: true, ExpectedScopes: 
[]string{"*devops.CicdScope"}},
+               {Name: "Azure DevOps Repository w/o Scope Config", Type: 
models.RepositoryTypeADO, Private: false,
+                       Entities: plugin.DOMAIN_TYPES, ExpectedScopes: 
[]string{"*code.Repo", "*ticket.Board", "*devops.CicdScope"}},
+               {Name: "Azure DevOps Repository w/ Scope Config", Type: 
models.RepositoryTypeADO, Private: false,
+                       Entities: []string{plugin.DOMAIN_TYPE_CODE}, 
ExpectedScopes: []string{"*code.Repo"}},
+               {Name: "Azure DevOps disabled Repository w/o Scope Config", 
Type: models.RepositoryTypeADO, Disabled: true,
+                       Entities: plugin.DOMAIN_TYPES, ExpectedScopes: 
[]string{"*ticket.Board", "*devops.CicdScope"}},
+               {Name: "Azure DevOps disabled Repository w/ Scope Config", 
Type: models.RepositoryTypeADO, Disabled: true,
+                       Entities: []string{plugin.DOMAIN_TYPE_CODE}, 
ExpectedScopes: []string{}},
+               {Name: "Azure DevOps disabled Repository w/ Scope Config", 
Type: models.RepositoryTypeADO, Disabled: true,
+                       Entities: []string{plugin.DOMAIN_TYPE_CROSS}, 
ExpectedScopes: []string{}},
+               {Name: "Azure DevOps disabled Repository w/ Scope Config", 
Type: models.RepositoryTypeADO, Disabled: true,
+                       Entities: []string{plugin.DOMAIN_TYPE_CICD}, 
ExpectedScopes: []string{"*devops.CicdScope"}},
+               {Name: "Public GitHub Repository", Type: 
models.RepositoryTypeGithub, Private: false,
+                       Entities: plugin.DOMAIN_TYPES, ExpectedScopes: 
[]string{"*code.Repo", "*devops.CicdScope", "*ticket.Board"}},
+               {Name: "Private GitHub Repository w/ Scope Config", Type: 
models.RepositoryTypeGithub, Private: true,
+                       Entities: plugin.DOMAIN_TYPES, ExpectedScopes: 
[]string{"*devops.CicdScope", "*ticket.Board"}},
+               {Name: "Private GitHub Repository w/o Scope Config", Type: 
models.RepositoryTypeGithub, Private: true,
+                       ExpectedScopes: []string{}},
        }
 
        for _, d := range data {
@@ -220,8 +244,7 @@ func TestMakeRemoteRepoScopes(t *testing.T) {
                                                },
                                                ScopeConfig: 
&models.AzuredevopsScopeConfig{
                                                        ScopeConfig: 
common.ScopeConfig{
-                                                               Entities: 
[]string{plugin.DOMAIN_TYPE_CODE, plugin.DOMAIN_TYPE_TICKET,
-                                                                       
plugin.DOMAIN_TYPE_CICD, plugin.DOMAIN_TYPE_CODE_REVIEW},
+                                                               Entities: 
d.Entities,
                                                        },
                                                },
                                        },
@@ -240,3 +263,118 @@ func TestMakeRemoteRepoScopes(t *testing.T) {
 
        }
 }
+
+func TestSubtasks(t *testing.T) {
+       mockAzuredevopsPlugin(t)
+
+       allSubtasks, err := sorter.NewTableSorter(tasks.SubTaskMetaList).Sort()
+       if err != nil {
+               t.Errorf("failed to sort subtasks: %v", err)
+       }
+
+       data := []struct {
+               Name          string
+               Type          string
+               Private       bool
+               Disabled      bool
+               Entities      []string // Data Entities configured in a scope 
config
+               ValidEntities []string
+       }{
+               {Name: "Active Azure DevOps Repository", Type: 
models.RepositoryTypeADO,
+                       Entities: plugin.DOMAIN_TYPES, ValidEntities: 
plugin.DOMAIN_TYPES},
+               {Name: "Disabled Azure DevOps Repository", Type: 
models.RepositoryTypeADO, Disabled: true, Entities: plugin.DOMAIN_TYPES,
+                       ValidEntities: []string{plugin.DOMAIN_TYPE_TICKET, 
plugin.DOMAIN_TYPE_CICD, plugin.DOMAIN_TYPE_CROSS}},
+               {Name: "Public GitHub Repository", Type: 
models.RepositoryTypeGithub, Entities: plugin.DOMAIN_TYPES,
+                       ValidEntities: []string{plugin.DOMAIN_TYPE_CICD, 
plugin.DOMAIN_TYPE_CROSS, plugin.DOMAIN_TYPE_CODE}},
+               {Name: "Private GitHub Repository", Type: 
models.RepositoryTypeGithub, Entities: plugin.DOMAIN_TYPES, Private: true,
+                       ValidEntities: []string{plugin.DOMAIN_TYPE_CICD, 
plugin.DOMAIN_TYPE_CROSS}},
+       }
+
+       for _, d := range data {
+               t.Run(d.Name, func(t *testing.T) {
+                       id := strings.ToLower(d.Name)
+                       id = strings.ReplaceAll(id, " ", "-")
+                       actualPlans, err := makePipelinePlanV200(
+                               allSubtasks,
+                               adoConnection(connectionID, azureDevOpsToken),
+                               
[]*srvhelper.ScopeDetail[models.AzuredevopsRepo, models.AzuredevopsScopeConfig]{
+                                       {
+                                               Scope:       adoRepo(d.Type, 
d.Private, d.Disabled),
+                                               ScopeConfig: 
adoScopeConfig(d.Entities),
+                                       },
+                               },
+                       )
+                       assert.Nil(t, err)
+
+                       validSubtasks, err := 
api.MakePipelinePlanSubtasks(allSubtasks, d.ValidEntities)
+                       assert.Nil(t, err)
+
+                       var count int
+                       for _, stage := range actualPlans {
+                               for _, task := range stage {
+                                       if task.Plugin == "azuredevops_go" {
+                                               for _, subtask := range 
task.Subtasks {
+                                                       assert.Contains(t, 
validSubtasks, subtask)
+                                                       count++
+                                               }
+                                               assert.Equal(t, count, 
len(validSubtasks))
+
+                                       }
+                               }
+                       }
+               })
+       }
+}
+
+func adoConnection(connectionID uint64, pat string) 
*models.AzuredevopsConnection {
+       return &models.AzuredevopsConnection{
+               BaseConnection: api.BaseConnection{
+                       Model: common.Model{
+                               ID: connectionID,
+                       },
+               },
+               AzuredevopsConn: models.AzuredevopsConn{
+                       AzuredevopsAccessToken: models.AzuredevopsAccessToken{
+                               Token: pat,
+                       },
+               },
+       }
+}
+
+func adoRepo(repoType string, isPrivate, isDisabled bool) 
models.AzuredevopsRepo {
+       const (
+               httpUrlToRepo          = "https://this_is_cloneUrl";
+               azureDevOpsProjectName = "azuredevops-test-project"
+               azureDevOpsOrgName     = "azuredevops-test-org"
+       )
+
+       return models.AzuredevopsRepo{
+               Id: fmt.Sprint(azuredevopsRepoId),
+               AzureDevOpsPK: models.AzureDevOpsPK{
+                       ProjectId:      azureDevOpsProjectName,
+                       OrganizationId: azureDevOpsOrgName,
+               },
+               Name:       azureDevOpsProjectName,
+               Url:        httpUrlToRepo,
+               RemoteUrl:  httpUrlToRepo,
+               Type:       repoType,
+               IsPrivate:  isPrivate,
+               IsDisabled: isDisabled,
+       }
+}
+
+func adoScopeConfig(entities []string) *models.AzuredevopsScopeConfig {
+       return &models.AzuredevopsScopeConfig{
+               ScopeConfig: common.ScopeConfig{
+                       Entities: entities,
+               },
+               DeploymentPattern: "(?i)deploy",
+               ProductionPattern: "(?i)prod",
+               Refdiff: map[string]interface{}{
+                       "tagsPattern": "pattern",
+                       "tagsLimit":   10,
+                       "tagsOrder":   "reverse semver",
+               },
+       }
+
+}
diff --git a/backend/plugins/azuredevops_go/api/init.go 
b/backend/plugins/azuredevops_go/api/init.go
index 0401e2b36..163398928 100644
--- a/backend/plugins/azuredevops_go/api/init.go
+++ b/backend/plugins/azuredevops_go/api/init.go
@@ -19,6 +19,7 @@ package api
 
 import (
        "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/log"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/azuredevops_go/models"
@@ -32,6 +33,7 @@ var dsHelper *api.DsHelper[models.AzuredevopsConnection, 
models.AzuredevopsRepo,
 var raProxy *api.DsRemoteApiProxyHelper[models.AzuredevopsConnection]
 var raScopeList *api.DsRemoteApiScopeListHelper[models.AzuredevopsConnection, 
models.AzuredevopsRepo, AzuredevopsRemotePagination]
 var raScopeSearch 
*api.DsRemoteApiScopeSearchHelper[models.AzuredevopsConnection, 
models.AzuredevopsRepo]
+var logger log.Logger
 
 func Init(br context.BasicRes, p plugin.PluginMeta) {
        vld = validator.New()
@@ -53,5 +55,5 @@ func Init(br context.BasicRes, p plugin.PluginMeta) {
        raProxy = 
api.NewDsRemoteApiProxyHelper[models.AzuredevopsConnection](dsHelper.ConnApi.ModelApiHelper)
        raScopeList = 
api.NewDsRemoteApiScopeListHelper[models.AzuredevopsConnection, 
models.AzuredevopsRepo, AzuredevopsRemotePagination](raProxy, 
listAzuredevopsRemoteScopes)
        raScopeSearch = 
api.NewDsRemoteApiScopeSearchHelper[models.AzuredevopsConnection, 
models.AzuredevopsRepo](raProxy, nil)
-
+       logger = br.GetLogger()
 }
diff --git a/backend/plugins/azuredevops_go/api/remote_helper.go 
b/backend/plugins/azuredevops_go/api/remote_helper.go
index 4cf26cba6..ee88c149e 100644
--- a/backend/plugins/azuredevops_go/api/remote_helper.go
+++ b/backend/plugins/azuredevops_go/api/remote_helper.go
@@ -151,12 +151,13 @@ func listAzuredevopsRepos(
        for _, v := range repos {
                pID := orgId + idSeparator + projectId
                repo := models.AzuredevopsRepo{
-                       Id:        v.Id,
-                       Type:      models.RepositoryTypeADO,
-                       Name:      v.Name,
-                       Url:       v.Url,
-                       RemoteUrl: v.RemoteUrl,
-                       IsFork:    false,
+                       Id:         v.Id,
+                       Type:       models.RepositoryTypeADO,
+                       Name:       v.Name,
+                       Url:        v.Url,
+                       RemoteUrl:  v.RemoteUrl,
+                       IsFork:     false,
+                       IsDisabled: v.IsDisabled,
                }
                repo.ProjectId = projectId
                repo.OrganizationId = orgId

Reply via email to