package jenkins import ( "fmt" "github.com/beevik/etree" devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" "strconv" "strings" "time" ) func replaceXmlVersion(config, oldVersion, targetVersion string) string { lines := strings.Split(config, "\n") lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1) output := strings.Join(lines, "\n") return output } func createPipelineConfigXml(pipeline *devopsv1alpha3.NoScmPipeline) (string, error) { doc := etree.NewDocument() xmlString := ` ` doc.ReadFromString(xmlString) flow := doc.SelectElement("flow-definition") flow.CreateElement("description").SetText(pipeline.Description) properties := flow.CreateElement("properties") if pipeline.DisableConcurrent { properties.CreateElement("org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") } if pipeline.Discarder != nil { discarder := properties.CreateElement("jenkins.model.BuildDiscarderProperty") strategy := discarder.CreateElement("strategy") strategy.CreateAttr("class", "hudson.tasks.LogRotator") strategy.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep) strategy.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep) strategy.CreateElement("artifactDaysToKeep").SetText("-1") strategy.CreateElement("artifactNumToKeep").SetText("-1") } if pipeline.Parameters != nil { appendParametersToEtree(properties, pipeline.Parameters) } if pipeline.TimerTrigger != nil { triggers := properties. CreateElement("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"). CreateElement("triggers") triggers.CreateElement("hudson.triggers.TimerTrigger").CreateElement("spec").SetText(pipeline.TimerTrigger.Cron) } pipelineDefine := flow.CreateElement("definition") pipelineDefine.CreateAttr("class", "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition") pipelineDefine.CreateAttr("plugin", "workflow-cps") pipelineDefine.CreateElement("script").SetText(pipeline.Jenkinsfile) pipelineDefine.CreateElement("sandbox").SetText("true") flow.CreateElement("triggers") if pipeline.RemoteTrigger != nil { flow.CreateElement("authToken").SetText(pipeline.RemoteTrigger.Token) } flow.CreateElement("disabled").SetText("false") doc.Indent(2) stringXml, err := doc.WriteToString() if err != nil { return "", err } return replaceXmlVersion(stringXml, "1.0", "1.1"), err } func parsePipelineConfigXml(config string) (*devopsv1alpha3.NoScmPipeline, error) { pipeline := &devopsv1alpha3.NoScmPipeline{} config = replaceXmlVersion(config, "1.1", "1.0") doc := etree.NewDocument() err := doc.ReadFromString(config) if err != nil { return nil, err } flow := doc.SelectElement("flow-definition") if flow == nil { return nil, fmt.Errorf("can not find pipeline definition") } pipeline.Description = flow.SelectElement("description").Text() properties := flow.SelectElement("properties") if properties. SelectElement( "org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") != nil { pipeline.DisableConcurrent = true } if properties.SelectElement("jenkins.model.BuildDiscarderProperty") != nil { strategy := properties. SelectElement("jenkins.model.BuildDiscarderProperty"). SelectElement("strategy") pipeline.Discarder = &devopsv1alpha3.DiscarderProperty{ DaysToKeep: strategy.SelectElement("daysToKeep").Text(), NumToKeep: strategy.SelectElement("numToKeep").Text(), } } pipeline.Parameters = getParametersfromEtree(properties) if len(pipeline.Parameters) == 0 { pipeline.Parameters = nil } if triggerProperty := properties. SelectElement( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil { triggers := triggerProperty.SelectElement("triggers") if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil { pipeline.TimerTrigger = &devopsv1alpha3.TimerTrigger{ Cron: timerTrigger.SelectElement("spec").Text(), } } } if authToken := flow.SelectElement("authToken"); authToken != nil { pipeline.RemoteTrigger = &devopsv1alpha3.RemoteTrigger{ Token: authToken.Text(), } } if definition := flow.SelectElement("definition"); definition != nil { if script := definition.SelectElement("script"); script != nil { pipeline.Jenkinsfile = script.Text() } } return pipeline, nil } func appendParametersToEtree(properties *etree.Element, parameters []devopsv1alpha3.Parameter) { parameterDefinitions := properties.CreateElement("hudson.model.ParametersDefinitionProperty"). CreateElement("parameterDefinitions") for _, parameter := range parameters { for className, typeName := range ParameterTypeMap { if typeName == parameter.Type { paramDefine := parameterDefinitions.CreateElement(className) paramDefine.CreateElement("name").SetText(parameter.Name) paramDefine.CreateElement("description").SetText(parameter.Description) switch parameter.Type { case "choice": choices := paramDefine.CreateElement("choices") choices.CreateAttr("class", "java.util.Arrays$ArrayList") a := choices.CreateElement("a") a.CreateAttr("class", "string-array") choiceValues := strings.Split(parameter.DefaultValue, "\n") for _, choiceValue := range choiceValues { a.CreateElement("string").SetText(choiceValue) } case "file": break default: paramDefine.CreateElement("defaultValue").SetText(parameter.DefaultValue) } } } } } func getParametersfromEtree(properties *etree.Element) []devopsv1alpha3.Parameter { var parameters []devopsv1alpha3.Parameter if parametersProperty := properties.SelectElement("hudson.model.ParametersDefinitionProperty"); parametersProperty != nil { params := parametersProperty.SelectElement("parameterDefinitions").ChildElements() for _, param := range params { switch param.Tag { case "hudson.model.StringParameterDefinition": parameters = append(parameters, devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("defaultValue").Text(), Type: ParameterTypeMap["hudson.model.StringParameterDefinition"], }) case "hudson.model.BooleanParameterDefinition": parameters = append(parameters, devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("defaultValue").Text(), Type: ParameterTypeMap["hudson.model.BooleanParameterDefinition"], }) case "hudson.model.TextParameterDefinition": parameters = append(parameters, devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("defaultValue").Text(), Type: ParameterTypeMap["hudson.model.TextParameterDefinition"], }) case "hudson.model.FileParameterDefinition": parameters = append(parameters, devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), Type: ParameterTypeMap["hudson.model.FileParameterDefinition"], }) case "hudson.model.PasswordParameterDefinition": parameters = append(parameters, devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("name").Text(), Type: ParameterTypeMap["hudson.model.PasswordParameterDefinition"], }) case "hudson.model.ChoiceParameterDefinition": choiceParameter := devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), Type: ParameterTypeMap["hudson.model.ChoiceParameterDefinition"], } choices := param.SelectElement("choices").SelectElement("a").SelectElements("string") for _, choice := range choices { choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text()) } choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue) parameters = append(parameters, choiceParameter) default: parameters = append(parameters, devopsv1alpha3.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: "unknown", Type: param.Tag, }) } } } return parameters } func appendGitSourceToEtree(source *etree.Element, gitSource *devopsv1alpha3.GitSource) { source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource") source.CreateAttr("plugin", "git") source.CreateElement("id").SetText(gitSource.ScmId) source.CreateElement("remote").SetText(gitSource.Url) if gitSource.CredentialId != "" { source.CreateElement("credentialsId").SetText(gitSource.CredentialId) } traits := source.CreateElement("traits") if gitSource.DiscoverBranches { traits.CreateElement("jenkins.plugins.git.traits.BranchDiscoveryTrait") } if gitSource.CloneOption != nil { cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(gitSource.CloneOption.Shallow)) cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("reference") if gitSource.CloneOption.Timeout >= 0 { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(gitSource.CloneOption.Timeout)) } else { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) } if gitSource.CloneOption.Depth >= 0 { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(gitSource.CloneOption.Depth)) } else { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) } } if gitSource.RegexFilter != "" { regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") regexTraits.CreateAttr("plugin", "scm-api@2.4.0") regexTraits.CreateElement("regex").SetText(gitSource.RegexFilter) } return } func getGitSourcefromEtree(source *etree.Element) *devopsv1alpha3.GitSource { var gitSource devopsv1alpha3.GitSource if credential := source.SelectElement("credentialsId"); credential != nil { gitSource.CredentialId = credential.Text() } if remote := source.SelectElement("remote"); remote != nil { gitSource.Url = remote.Text() } traits := source.SelectElement("traits") if branchDiscoverTrait := traits.SelectElement( "jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil { gitSource.DiscoverBranches = true } if cloneTrait := traits.SelectElement( "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { if cloneExtension := cloneTrait.SelectElement( "extension"); cloneExtension != nil { gitSource.CloneOption = &devopsv1alpha3.GitCloneOption{} if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { gitSource.CloneOption.Shallow = value } if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { gitSource.CloneOption.Timeout = int(value) } if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { gitSource.CloneOption.Depth = int(value) } } } if regexTrait := traits.SelectElement( "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { if regex := regexTrait.SelectElement("regex"); regex != nil { gitSource.RegexFilter = regex.Text() } } return &gitSource } func getGithubSourcefromEtree(source *etree.Element) *devopsv1alpha3.GithubSource { var githubSource devopsv1alpha3.GithubSource if credential := source.SelectElement("credentialsId"); credential != nil { githubSource.CredentialId = credential.Text() } if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { githubSource.Owner = repoOwner.Text() } if repository := source.SelectElement("repository"); repository != nil { githubSource.Repo = repository.Text() } if apiUri := source.SelectElement("apiUri"); apiUri != nil { githubSource.ApiUri = apiUri.Text() } traits := source.SelectElement("traits") if branchDiscoverTrait := traits.SelectElement( "org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil { strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) githubSource.DiscoverBranches = strategyId } if originPRDiscoverTrait := traits.SelectElement( "org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) githubSource.DiscoverPRFromOrigin = strategyId } if forkPRDiscoverTrait := traits.SelectElement( "org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text()) trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value trust := strings.Split(trustClass, "$") switch trust[1] { case "TrustContributors": githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 1, } case "TrustEveryone": githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 2, } case "TrustPermission": githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 3, } case "TrustNobody": githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 4, } } if cloneTrait := traits.SelectElement( "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { if cloneExtension := cloneTrait.SelectElement( "extension"); cloneExtension != nil { githubSource.CloneOption = &devopsv1alpha3.GitCloneOption{} if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { githubSource.CloneOption.Shallow = value } if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { githubSource.CloneOption.Timeout = int(value) } if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { githubSource.CloneOption.Depth = int(value) } } } if regexTrait := traits.SelectElement( "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { if regex := regexTrait.SelectElement("regex"); regex != nil { githubSource.RegexFilter = regex.Text() } } } return &githubSource } func appendGithubSourceToEtree(source *etree.Element, githubSource *devopsv1alpha3.GithubSource) { source.CreateAttr("class", "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource") source.CreateAttr("plugin", "github-branch-source") source.CreateElement("id").SetText(githubSource.ScmId) source.CreateElement("credentialsId").SetText(githubSource.CredentialId) source.CreateElement("repoOwner").SetText(githubSource.Owner) source.CreateElement("repository").SetText(githubSource.Repo) if githubSource.ApiUri != "" { source.CreateElement("apiUri").SetText(githubSource.ApiUri) } traits := source.CreateElement("traits") if githubSource.DiscoverBranches != 0 { traits.CreateElement("org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"). CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverBranches)) } if githubSource.DiscoverPRFromOrigin != 0 { traits.CreateElement("org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"). CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromOrigin)) } if githubSource.DiscoverPRFromForks != nil { forkTrait := traits.CreateElement("org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait") forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromForks.Strategy)) trustClass := "org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$" switch githubSource.DiscoverPRFromForks.Trust { case 1: trustClass += "TrustContributors" case 2: trustClass += "TrustEveryone" case 3: trustClass += "TrustPermission" case 4: trustClass += "TrustNobody" default: } forkTrait.CreateElement("trust").CreateAttr("class", trustClass) } if githubSource.CloneOption != nil { cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(githubSource.CloneOption.Shallow)) cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("reference") if githubSource.CloneOption.Timeout >= 0 { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(githubSource.CloneOption.Timeout)) } else { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) } if githubSource.CloneOption.Depth >= 0 { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(githubSource.CloneOption.Depth)) } else { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) } } if githubSource.RegexFilter != "" { regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") regexTraits.CreateAttr("plugin", "scm-api@2.4.0") regexTraits.CreateElement("regex").SetText(githubSource.RegexFilter) } return } func getBitbucketServerSourceFromEtree(source *etree.Element) *devopsv1alpha3.BitbucketServerSource { var s devopsv1alpha3.BitbucketServerSource if credential := source.SelectElement("credentialsId"); credential != nil { s.CredentialId = credential.Text() } if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { s.Owner = repoOwner.Text() } if repository := source.SelectElement("repository"); repository != nil { s.Repo = repository.Text() } if apiUri := source.SelectElement("serverUrl"); apiUri != nil { s.ApiUri = apiUri.Text() } traits := source.SelectElement("traits") if branchDiscoverTrait := traits.SelectElement( "com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait"); branchDiscoverTrait != nil { strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) s.DiscoverBranches = strategyId } if originPRDiscoverTrait := traits.SelectElement( "com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) s.DiscoverPRFromOrigin = strategyId } if forkPRDiscoverTrait := traits.SelectElement( "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text()) trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value trust := strings.Split(trustClass, "$") switch trust[1] { case "TrustEveryone": s.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 1, } case "TrustTeamForks": s.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 2, } case "TrustNobody": s.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ Strategy: strategyId, Trust: 3, } } if cloneTrait := traits.SelectElement( "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { if cloneExtension := cloneTrait.SelectElement( "extension"); cloneExtension != nil { s.CloneOption = &devopsv1alpha3.GitCloneOption{} if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { s.CloneOption.Shallow = value } if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { s.CloneOption.Timeout = int(value) } if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { s.CloneOption.Depth = int(value) } } } if regexTrait := traits.SelectElement( "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { if regex := regexTrait.SelectElement("regex"); regex != nil { s.RegexFilter = regex.Text() } } } return &s } func appendBitbucketServerSourceToEtree(source *etree.Element, s *devopsv1alpha3.BitbucketServerSource) { source.CreateAttr("class", "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource") source.CreateAttr("plugin", "cloudbees-bitbucket-branch-source") source.CreateElement("id").SetText(s.ScmId) source.CreateElement("credentialsId").SetText(s.CredentialId) source.CreateElement("repoOwner").SetText(s.Owner) source.CreateElement("repository").SetText(s.Repo) source.CreateElement("serverUrl").SetText(s.ApiUri) traits := source.CreateElement("traits") if s.DiscoverBranches != 0 { traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait>"). CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverBranches)) } if s.DiscoverPRFromOrigin != 0 { traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"). CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromOrigin)) } if s.DiscoverPRFromForks != nil { forkTrait := traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait") forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromForks.Strategy)) trustClass := "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait$" switch s.DiscoverPRFromForks.Trust { case 1: trustClass += "TrustEveryone" case 2: trustClass += "TrustTeamForks" case 3: trustClass += "TrustNobody" default: trustClass += "TrustEveryone" } forkTrait.CreateElement("trust").CreateAttr("class", trustClass) } if s.CloneOption != nil { cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("reference") if s.CloneOption.Timeout >= 0 { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) } else { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) } if s.CloneOption.Depth >= 0 { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) } else { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) } } if s.RegexFilter != "" { regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") regexTraits.CreateAttr("plugin", "scm-api@2.4.0") regexTraits.CreateElement("regex").SetText(s.RegexFilter) } return } func getSvnSourcefromEtree(source *etree.Element) *devopsv1alpha3.SvnSource { var s devopsv1alpha3.SvnSource if remote := source.SelectElement("remoteBase"); remote != nil { s.Remote = remote.Text() } if credentialsId := source.SelectElement("credentialsId"); credentialsId != nil { s.CredentialId = credentialsId.Text() } if includes := source.SelectElement("includes"); includes != nil { s.Includes = includes.Text() } if excludes := source.SelectElement("excludes"); excludes != nil { s.Excludes = excludes.Text() } return &s } func appendSvnSourceToEtree(source *etree.Element, s *devopsv1alpha3.SvnSource) { source.CreateAttr("class", "jenkins.scm.impl.subversion.SubversionSCMSource") source.CreateAttr("plugin", "subversion") source.CreateElement("id").SetText(s.ScmId) if s.CredentialId != "" { source.CreateElement("credentialsId").SetText(s.CredentialId) } if s.Remote != "" { source.CreateElement("remoteBase").SetText(s.Remote) } if s.Includes != "" { source.CreateElement("includes").SetText(s.Includes) } if s.Excludes != "" { source.CreateElement("excludes").SetText(s.Excludes) } return } func getSingleSvnSourceFromEtree(source *etree.Element) *devopsv1alpha3.SingleSvnSource { var s devopsv1alpha3.SingleSvnSource if scm := source.SelectElement("scm"); scm != nil { if locations := scm.SelectElement("locations"); locations != nil { if moduleLocations := locations.SelectElement("hudson.scm.SubversionSCM_-ModuleLocation"); moduleLocations != nil { if remote := moduleLocations.SelectElement("remote"); remote != nil { s.Remote = remote.Text() } if credentialId := moduleLocations.SelectElement("credentialsId"); credentialId != nil { s.CredentialId = credentialId.Text() } } } } return &s } func appendSingleSvnSourceToEtree(source *etree.Element, s *devopsv1alpha3.SingleSvnSource) { source.CreateAttr("class", "jenkins.scm.impl.SingleSCMSource") source.CreateAttr("plugin", "scm-api") source.CreateElement("id").SetText(s.ScmId) source.CreateElement("name").SetText("master") scm := source.CreateElement("scm") scm.CreateAttr("class", "hudson.scm.SubversionSCM") scm.CreateAttr("plugin", "subversion") location := scm.CreateElement("locations").CreateElement("hudson.scm.SubversionSCM_-ModuleLocation") if s.Remote != "" { location.CreateElement("remote").SetText(s.Remote) } if s.CredentialId != "" { location.CreateElement("credentialsId").SetText(s.CredentialId) } location.CreateElement("local").SetText(".") location.CreateElement("depthOption").SetText("infinity") location.CreateElement("ignoreExternalsOption").SetText("true") location.CreateElement("cancelProcessOnExternalsFail").SetText("true") source.CreateElement("excludedRegions") source.CreateElement("includedRegions") source.CreateElement("excludedUsers") source.CreateElement("excludedRevprop") source.CreateElement("excludedCommitMessages") source.CreateElement("workspaceUpdater").CreateAttr("class", "hudson.scm.subversion.UpdateUpdater") source.CreateElement("ignoreDirPropChanges").SetText("false") source.CreateElement("filterChangelog").SetText("false") source.CreateElement("quietOperation").SetText("true") return } func appendMultiBranchJobTriggerToEtree(properties *etree.Element, s *devopsv1alpha3.MultiBranchJobTrigger) { triggerProperty := properties.CreateElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") triggerProperty.CreateAttr("plugin", "multibranch-action-triggers") triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger) triggerProperty.CreateElement("deleteActionJobsToTrigger").SetText(s.DeleteActionJobsToTrigger) return } func getMultiBranchJobTriggerfromEtree(properties *etree.Element) *devopsv1alpha3.MultiBranchJobTrigger { var s devopsv1alpha3.MultiBranchJobTrigger triggerProperty := properties.SelectElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") if triggerProperty != nil { s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").Text() s.DeleteActionJobsToTrigger = triggerProperty.SelectElement("deleteActionJobsToTrigger").Text() } return &s } func createMultiBranchPipelineConfigXml(projectName string, pipeline *devopsv1alpha3.MultiBranchPipeline) (string, error) { doc := etree.NewDocument() xmlString := ` false ` err := doc.ReadFromString(xmlString) if err != nil { return "", err } project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") project.CreateElement("description").SetText(pipeline.Description) if pipeline.MultiBranchJobTrigger != nil { properties := project.SelectElement("properties") appendMultiBranchJobTriggerToEtree(properties, pipeline.MultiBranchJobTrigger) } if pipeline.Discarder != nil { discarder := project.CreateElement("orphanedItemStrategy") discarder.CreateAttr("class", "com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy") discarder.CreateAttr("plugin", "cloudbees-folder") discarder.CreateElement("pruneDeadBranches").SetText("true") discarder.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep) discarder.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep) } triggers := project.CreateElement("triggers") if pipeline.TimerTrigger != nil { timeTrigger := triggers.CreateElement( "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger") timeTrigger.CreateAttr("plugin", "cloudbees-folder") millis, err := strconv.ParseInt(pipeline.TimerTrigger.Interval, 10, 64) if err != nil { return "", err } timeTrigger.CreateElement("spec").SetText(toCrontab(millis)) timeTrigger.CreateElement("interval").SetText(pipeline.TimerTrigger.Interval) triggers.CreateElement("disabled").SetText("false") } sources := project.CreateElement("sources") sources.CreateAttr("class", "jenkins.branch.MultiBranchProject$BranchSourceList") sources.CreateAttr("plugin", "branch-api") sourcesOwner := sources.CreateElement("owner") sourcesOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") sourcesOwner.CreateAttr("reference", "../..") branchSource := sources.CreateElement("data").CreateElement("jenkins.branch.BranchSource") branchSourceStrategy := branchSource.CreateElement("strategy") branchSourceStrategy.CreateAttr("class", "jenkins.branch.NamedExceptionsBranchPropertyStrategy") branchSourceStrategy.CreateElement("defaultProperties").CreateAttr("class", "empty-list") branchSourceStrategy.CreateElement("namedExceptions").CreateAttr("class", "empty-list") source := branchSource.CreateElement("source") switch pipeline.SourceType { case "git": appendGitSourceToEtree(source, pipeline.GitSource) case "github": appendGithubSourceToEtree(source, pipeline.GitHubSource) case "svn": appendSvnSourceToEtree(source, pipeline.SvnSource) case "single_svn": appendSingleSvnSourceToEtree(source, pipeline.SingleSvnSource) case "bitbucket_server": appendBitbucketServerSourceToEtree(source, pipeline.BitbucketServerSource) default: return "", fmt.Errorf("unsupport source type") } factory := project.CreateElement("factory") factory.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory") factoryOwner := factory.CreateElement("owner") factoryOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") factoryOwner.CreateAttr("reference", "../..") factory.CreateElement("scriptPath").SetText(pipeline.ScriptPath) doc.Indent(2) stringXml, err := doc.WriteToString() return replaceXmlVersion(stringXml, "1.0", "1.1"), err } func parseMultiBranchPipelineConfigXml(config string) (*devopsv1alpha3.MultiBranchPipeline, error) { pipeline := &devopsv1alpha3.MultiBranchPipeline{} config = replaceXmlVersion(config, "1.1", "1.0") doc := etree.NewDocument() err := doc.ReadFromString(config) if err != nil { return nil, err } project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") if project == nil { return nil, fmt.Errorf("can not parse mutibranch pipeline config") } if properties := project.SelectElement("properties"); properties != nil { if multibranchTrigger := properties.SelectElement( "org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil { pipeline.MultiBranchJobTrigger = getMultiBranchJobTriggerfromEtree(properties) } } pipeline.Description = project.SelectElement("description").Text() if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil { pipeline.Discarder = &devopsv1alpha3.DiscarderProperty{ DaysToKeep: discarder.SelectElement("daysToKeep").Text(), NumToKeep: discarder.SelectElement("numToKeep").Text(), } } if triggers := project.SelectElement("triggers"); triggers != nil { if timerTrigger := triggers.SelectElement( "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil { pipeline.TimerTrigger = &devopsv1alpha3.TimerTrigger{ Interval: timerTrigger.SelectElement("interval").Text(), } } } if sources := project.SelectElement("sources"); sources != nil { if sourcesData := sources.SelectElement("data"); sourcesData != nil { if branchSource := sourcesData.SelectElement("jenkins.branch.BranchSource"); branchSource != nil { source := branchSource.SelectElement("source") switch source.SelectAttr("class").Value { case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource": pipeline.GitHubSource = getGithubSourcefromEtree(source) pipeline.SourceType = "github" case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource": pipeline.BitbucketServerSource = getBitbucketServerSourceFromEtree(source) pipeline.SourceType = "bitbucket_server" case "jenkins.plugins.git.GitSCMSource": pipeline.SourceType = "git" pipeline.GitSource = getGitSourcefromEtree(source) case "jenkins.scm.impl.SingleSCMSource": pipeline.SourceType = "single_svn" pipeline.SingleSvnSource = getSingleSvnSourceFromEtree(source) case "jenkins.scm.impl.subversion.SubversionSCMSource": pipeline.SourceType = "svn" pipeline.SvnSource = getSvnSourcefromEtree(source) } } } } pipeline.ScriptPath = project.SelectElement("factory").SelectElement("scriptPath").Text() return pipeline, nil } func toCrontab(millis int64) string { if millis*time.Millisecond.Nanoseconds() <= 5*time.Minute.Nanoseconds() { return "* * * * *" } if millis*time.Millisecond.Nanoseconds() <= 30*time.Minute.Nanoseconds() { return "H/5 * * * *" } if millis*time.Millisecond.Nanoseconds() <= 1*time.Hour.Nanoseconds() { return "H/15 * * * *" } if millis*time.Millisecond.Nanoseconds() <= 8*time.Hour.Nanoseconds() { return "H/30 * * * *" } if millis*time.Millisecond.Nanoseconds() <= 24*time.Hour.Nanoseconds() { return "H H/4 * * *" } if millis*time.Millisecond.Nanoseconds() <= 48*time.Hour.Nanoseconds() { return "H H/12 * * *" } return "H H * * *" }