diff --git a/command/rio.go b/command/rio.go new file mode 100644 index 00000000..30203e27 --- /dev/null +++ b/command/rio.go @@ -0,0 +1,132 @@ +// Copyright 2022 Harness, Inc. +// +// Licensed 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 command + +import ( + "context" + "flag" + "fmt" + "github.com/drone/go-convert/convert/rio" + "io/ioutil" + "log" + "os" + + "github.com/google/subcommands" +) + +type Rio struct { + name string + proj string + org string + repoName string + repoConn string + kubeName string + kubeConn string + dockerConn string + userGroup string + githubConn string + + downgrade bool + beforeAfter bool +} + +func (*Rio) Name() string { return "rio" } +func (*Rio) Synopsis() string { return "converts a rio pipeline" } +func (*Rio) Usage() string { + return `rio [-downgrade] +` +} + +func (c *Rio) SetFlags(f *flag.FlagSet) { + f.BoolVar(&c.downgrade, "downgrade", false, "downgrade to the legacy yaml format") + f.BoolVar(&c.beforeAfter, "before-after", false, "print the befor and after") + + f.StringVar(&c.org, "org", "default", "harness organization") + f.StringVar(&c.proj, "project", "default", "harness project") + f.StringVar(&c.name, "pipeline", "default", "harness pipeline name") + f.StringVar(&c.repoConn, "repo-connector", "", "repository connector") + f.StringVar(&c.repoName, "repo-name", "", "repository name") + f.StringVar(&c.kubeConn, "kube-connector", "", "kubernetes connector") + f.StringVar(&c.kubeName, "kube-namespace", "", "kubernets namespace") + f.StringVar(&c.dockerConn, "docker-connector", "", "dockerhub connector") + f.StringVar(&c.userGroup, "notification-user-group", "", "notification-user-group") + f.StringVar(&c.githubConn, "github-connector", "", "github-connector") +} + +func (c *Rio) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + path := f.Arg(0) + + fmt.Println("# v1 harness yaml is not supported for Rio pipelines. Converting to v0...") + // if the user does not specify the path as + // a command line arg, assume the default path. + if path == "" { + path = ".rio.yml" + } + + // open the rio yaml + before, err := ioutil.ReadFile(path) + if err != nil { + log.Println(err) + return subcommands.ExitFailure + } + + // convert the pipeline yaml from the rio + // format to the harness yaml format. + converter := rio.New( + rio.WithDockerhub(c.dockerConn), + rio.WithKubernetes(c.kubeName, c.kubeConn), + rio.WithName(c.name), + rio.WithIdentifier(c.name), + rio.WithOrganization(c.org), + rio.WithProject(c.proj), + rio.WithNotifyUserGroup(c.userGroup), + rio.WithGithubConnector(c.githubConn), + ) + after, err := converter.ConvertBytes(before) + if err != nil { + log.Println(err) + return subcommands.ExitFailure + } + + // downgrade from the v1 harness yaml format + // to the v0 harness yaml format. + if c.downgrade { + fmt.Println("# downgrade for Rio pipeline is not supported") + //// downgrade to the v0 yaml + //d := downgrader.New( + // downgrader.WithCodebase(c.repoName, c.repoConn), + // downgrader.WithDockerhub(c.dockerConn), + // downgrader.WithKubernetes(c.kubeName, c.kubeName), + // downgrader.WithName(c.name), + // downgrader.WithOrganization(c.org), + // downgrader.WithProject(c.proj), + //) + //after, err = d.Downgrade(after) + //if err != nil { + // log.Println(err) + // return subcommands.ExitFailure + //} + } + + if c.beforeAfter { + os.Stdout.WriteString("---\n") + os.Stdout.Write(before) + os.Stdout.WriteString("\n---\n") + } + + os.Stdout.Write(after) + + return subcommands.ExitSuccess +} diff --git a/convert/harness/yaml/const.go b/convert/harness/yaml/const.go index 6801c94d..dc3f125c 100644 --- a/convert/harness/yaml/const.go +++ b/convert/harness/yaml/const.go @@ -14,6 +14,10 @@ package yaml +const ( + DefaultDockerConnector = "account.harnessImage" +) + type WhenStatus string const ( @@ -85,7 +89,7 @@ type Shell string const ( ShellNone Shell = "None" ShellBash = "Bash" - ShellPosix = "Shell" + ShellPosix = "Sh" ShellPowershell = "Powershell" ) @@ -97,3 +101,11 @@ const ( ImagePullIfNotPresent = "IfNotPresent" ImagePullNever = "Never" ) + +type InfraOs string + +const ( + InfraOsLinux InfraOs = "Linux" + InfraOsMac = "MacOS" + InfraOsWindows = "Windows" +) diff --git a/convert/harness/yaml/pipeline.go b/convert/harness/yaml/pipeline.go index 241ddadb..3bc99d79 100644 --- a/convert/harness/yaml/pipeline.go +++ b/convert/harness/yaml/pipeline.go @@ -22,15 +22,17 @@ type ( // Pipeline defines a pipeline. Pipeline struct { - ID string `json:"identifier,omitempty" yaml:"identifier,omitempty"` - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Desc string `json:"description,omitempty" yaml:"description,omitempty"` - Account string `json:"accountIdentifier,omitempty" yaml:"accountIdentifier,omitempty"` - Project string `json:"projectIdentifier,omitempty" yaml:"projectIdentifier,omitempty"` - Org string `json:"orgIdentifier,omitempty" yaml:"orgIdentifier,omitempty"` - Props Properties `json:"properties,omitempty" yaml:"properties,omitempty"` - Stages []*Stages `json:"stages,omitempty" yaml:"stages"` - Variables []*Variable `json:"variables,omitempty" yaml:"variables,omitempty"` + ID string `json:"identifier,omitempty" yaml:"identifier,omitempty"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Desc string `json:"description,omitempty" yaml:"description,omitempty"` + Account string `json:"accountIdentifier,omitempty" yaml:"accountIdentifier,omitempty"` + Project string `json:"projectIdentifier,omitempty" yaml:"projectIdentifier,omitempty"` + Org string `json:"orgIdentifier,omitempty" yaml:"orgIdentifier,omitempty"` + Props Properties `json:"properties,omitempty" yaml:"properties,omitempty"` + Stages []*Stages `json:"stages,omitempty" yaml:"stages"` + Variables []*Variable `json:"variables,omitempty" yaml:"variables,omitempty"` + Timeout string `json:"timeout,omitempty" yaml:"timeout,omitempty"` + NotificationRules []NotificationRules `json:"notificationRules,omitempty" yaml:"notificationRules,omitempty"` } // Properties defines pipeline properties. @@ -69,10 +71,13 @@ type ( Spec *InfraSpec `json:"spec,omitempty" yaml:"spec,omitempty"` } - // InfraSpec describes pipeline infastructure. + // InfraSpec describes pipeline infrastructure. InfraSpec struct { - Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` - Conn string `json:"connectorRef,omitempty" yaml:"connectorRef,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Conn string `json:"connectorRef,omitempty" yaml:"connectorRef,omitempty"` + AutomountServiceToken bool `json:"automountServiceAccountToken,omitempty" yaml:"automountServiceAccountToken,omitempty"` + Os string `json:"os,omitempty" yaml:"os,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"` } Platform struct { @@ -134,4 +139,25 @@ type ( Memory BytesSize `json:"memory,omitempty" yaml:"memory,omitempty"` CPU MilliSize `json:"cpu,omitempty" yaml:"cpu,omitempty"` // TODO } + + NotificationRules struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Id string `json:"identifier,omitempty" yaml:"identifier,omitempty"` + PipelineEvents []NotificationPipelineEvent `json:"pipelineEvents,omitempty" yaml:"pipelineEvents,omitempty"` + NotificationMethod NotificationMethod `json:"notificationMethod,omitempty" yaml:"notificationMethod,omitempty"` + Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` + } + + NotificationPipelineEvent struct { + Type string `json:"type,omitempty" yaml:"type,omitempty"` + } + + NotificationMethod struct { + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Spec NotificationSpec `json:"spec,omitempty" yaml:"spec,omitempty"` + } + + NotificationSpec struct { + UserGroups []string `json:"userGroups,omitempty" yaml:"userGroups,omitempty"` + } ) diff --git a/convert/harness/yaml/step.go b/convert/harness/yaml/step.go index 4131528a..2ab228f8 100644 --- a/convert/harness/yaml/step.go +++ b/convert/harness/yaml/step.go @@ -101,7 +101,7 @@ type ( Reports []*Report `json:"reports,omitempty" yaml:"reports,omitempty"` Resources *Resources `json:"resources,omitempty" yaml:"resources,omitempty"` RunAsUser string `json:"runAsUser,omitempty" yaml:"runAsUser,omitempty"` - Tags map[string]string `json:"tags,omitempty" yaml:"tags,omitempty"` + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` Target string `json:"target,omitempty" yaml:"target,omitempty"` } diff --git a/convert/rio/convert.go b/convert/rio/convert.go new file mode 100644 index 00000000..1c579976 --- /dev/null +++ b/convert/rio/convert.go @@ -0,0 +1,322 @@ +package rio + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + + harness "github.com/drone/go-convert/convert/harness/yaml" + rio "github.com/drone/go-convert/convert/rio/yaml" + "github.com/drone/go-convert/internal/store" + "gopkg.in/yaml.v3" +) + +// as we walk the yaml, we store a +// snapshot of the current node and +// its parents. +type context struct { + config *rio.Config +} + +// Converter converts a Rio pipeline to a Harness +// v0 pipeline. +type Converter struct { + kubeEnabled bool + kubeNamespace string + kubeConnector string + kubeOs string + dockerhubConn string + githubConnector string + identifiers *store.Identifiers + + pipelineId string + pipelineName string + pipelineOrg string + pipelineProj string + + notifyUserGroup string +} + +// New creates a new Converter that converts a Rio +// pipeline to a Harness v1 pipeline. +func New(options ...Option) *Converter { + d := new(Converter) + + // create the unique identifier store. this store + // is used for registering unique identifiers to + // prevent duplicate names, unique index violations. + d.identifiers = store.New() + + // loop through and apply the options. + for _, option := range options { + option(d) + } + + // set the default kubernetes namespace. + if d.kubeNamespace == "" { + d.kubeNamespace = "default" + } + + // set default kubernetes OS + if d.kubeOs == "" { + d.kubeOs = string(harness.InfraOsLinux) + } + + // set the runtime to kubernetes if the kubernetes + // connector is configured. + if d.kubeConnector != "" { + d.kubeEnabled = true + } + + // set default docker connector + if d.dockerhubConn == "" { + d.dockerhubConn = harness.DefaultDockerConnector + } + + if d.notifyUserGroup == "" { + d.notifyUserGroup = "account._account_all_users" + } + return d +} + +// Convert converts a rio pipeline to v0. +func (d *Converter) Convert(r io.Reader) ([]byte, error) { + config, err := rio.Parse(r) + if err != nil { + return nil, err + } + return d.convert(&context{ + config: config, + }) +} + +// ConvertBytes converts a rio pipeline to v0. +func (d *Converter) ConvertBytes(b []byte) ([]byte, error) { + return d.Convert( + bytes.NewBuffer(b), + ) +} + +// ConvertString converts a rio pipeline to v0. +func (d *Converter) ConvertString(s string) ([]byte, error) { + return d.Convert( + bytes.NewBufferString(s), + ) +} + +// ConvertFile converts a rio pipeline to v0. +func (d *Converter) ConvertFile(p string) ([]byte, error) { + f, err := os.Open(p) + if err != nil { + return nil, err + } + defer f.Close() + return d.Convert(f) +} + +func (d *Converter) convertRunStep(p rio.Pipeline) harness.StepRun { + step := new(harness.StepRun) + step.Env = p.Machine.Env + command := "" + for _, s := range p.Build.Steps { + command += fmt.Sprintf("%s\n", s) + } + step.Command = command + step.Shell = harness.ShellPosix + step.ConnRef = d.dockerhubConn + step.Image = p.Machine.BaseImage + return *step +} + +func (d *Converter) convertDockerSteps(dockerfile rio.Dockerfile) []harness.StepDocker { + steps := make([]harness.StepDocker, 0) + if dockerfile.Version == "" { + dockerfile.Version = "latest" + } + tags := dockerfile.ExtraTags + tags = append(tags, dockerfile.Version) + for _, r := range dockerfile.Publish { + step := new(harness.StepDocker) + step.Context = dockerfile.Context + step.Dockerfile = dockerfile.DockerfilePath + step.Repo = r.Repo + step.Tags = tags + step.BuildsArgs = dockerfile.Env + step.ConnectorRef = d.dockerhubConn + steps = append(steps, *step) + } + return steps +} + +func (d *Converter) convertExecution(p rio.Pipeline) harness.Execution { + execution := harness.Execution{} + + executionSteps := make([]*harness.Steps, 0) + // Append all commands in build attribute to one run step + if len(p.Build.Steps) != 0 { + steps := harness.Steps{ + Step: &harness.Step{ + Name: "Build", + ID: "Build", + Spec: d.convertRunStep(p), + Type: harness.StepTypeRun, + }, + } + executionSteps = append(executionSteps, &steps) + } + + dockerStepCounter := 1 + if len(p.Pkg.Dockerfile) != 0 { + if !p.Pkg.Release { + fmt.Println("# [WARN]: release=false is not supported") + } + + // Each entry in package.Dockerfile attribute is one build and push step + for _, dockerfile := range p.Pkg.Dockerfile { + // each dockerfile entry can have multiple repos + dockerSteps := d.convertDockerSteps(dockerfile) + + // Append all docker steps + for _, dStep := range dockerSteps { + steps := harness.Steps{ + Step: &harness.Step{ + Name: fmt.Sprintf("BuildAndPush_%d", dockerStepCounter), + ID: fmt.Sprintf("BuildAndPush_%d", dockerStepCounter), + Spec: &dStep, + Type: harness.StepTypeBuildAndPushDockerRegistry, + }, + } + executionSteps = append(executionSteps, &steps) + dockerStepCounter++ + } + } + } + execution.Steps = executionSteps + return execution +} + +func (d *Converter) convertCIStage(p rio.Pipeline, platform string) harness.StageCI { + stage := harness.StageCI{ + Execution: d.convertExecution(p), + } + if d.kubeEnabled { + infra := harness.Infrastructure{ + Type: harness.InfraTypeKubernetesDirect, + Spec: &harness.InfraSpec{ + Namespace: d.kubeNamespace, + Conn: d.kubeConnector, + AutomountServiceToken: true, + Os: d.kubeOs, + }, + } + if platform == "linux/amd64" { + infra.Spec.NodeSelector = map[string]string{"kubernetes.io/arch": "amd64"} + infra.Spec.Os = string(harness.InfraOsLinux) + } else if platform == "linux/arm64" { + infra.Spec.NodeSelector = map[string]string{"kubernetes.io/arch": "arm64"} + infra.Spec.Os = string(harness.InfraOsLinux) + } + stage.Infrastructure = &infra + } + if d.githubConnector != "" { + stage.Clone = true + } + return stage +} + +func (d *Converter) getStages(p rio.Pipeline) *harness.Stages { + if len(p.Machine.TargetPlatforms) == 0 { + // assume linux/amd64 - one stage + return &harness.Stages{ + Stage: &harness.Stage{ + Name: p.Name, + ID: d.convertNameToID(p.Name), + Spec: d.convertCIStage(p, ""), + Type: harness.StageTypeCI, + }, + } + } + // parallel stages with different target platforms + stages := make([]*harness.Stages, 0) + for _, platform := range p.Machine.TargetPlatforms { + stageName := fmt.Sprintf("%s_%s", p.Name, d.convertNameToID(platform)) + stage := &harness.Stages{ + Stage: &harness.Stage{ + Name: stageName, + ID: d.convertNameToID(stageName), + Spec: d.convertCIStage(p, platform), + Type: harness.StageTypeCI, + }, + } + stages = append(stages, stage) + } + return &harness.Stages{ + Parallel: stages, + } +} + +func (d *Converter) convertNameToID(name string) (ID string) { + ID = strings.ReplaceAll(name, " ", "_") + ID = strings.ReplaceAll(ID, "-", "_") + ID = strings.ReplaceAll(ID, "/", "_") + return ID +} + +func (d *Converter) getNotifications() []harness.NotificationRules { + notificationRules := []harness.NotificationRules{ + { + Name: "user_group_notification", + Id: "user_group_notification", + PipelineEvents: []harness.NotificationPipelineEvent{ + {Type: "PipelineSuccess"}, + {Type: "PipelineFailed"}, + }, + NotificationMethod: harness.NotificationMethod{ + Type: "Email", + Spec: harness.NotificationSpec{ + UserGroups: []string{d.notifyUserGroup}, + }, + }, + Enabled: true, + }, + } + return notificationRules +} + +// converts converts a Rio pipeline to a Harness pipeline. +func (d *Converter) convert(ctx *context) ([]byte, error) { + // create the harness pipeline spec + pipeline := &harness.Pipeline{} + for _, p := range ctx.config.Pipelines { + pipeline.Stages = append(pipeline.Stages, d.getStages(p)) + } + pipeline.Name = d.pipelineName + pipeline.ID = d.pipelineId + pipeline.Org = d.pipelineOrg + pipeline.Project = d.pipelineProj + if ctx.config.Timeout != 0 { + pipeline.Timeout = fmt.Sprintf("%dm", ctx.config.Timeout) + } + if ctx.config.Notify.Email.Enabled { + pipeline.NotificationRules = d.getNotifications() + } + if d.githubConnector != "" { + pipeline.Props.CI.Codebase = harness.Codebase{ + Conn: d.githubConnector, + Build: "<+input>", + } + } + + // create the harness pipeline resource + config := &harness.Config{Pipeline: *pipeline} + + // marshal the harness yaml + out, err := yaml.Marshal(config) + if err != nil { + return nil, err + } + + return out, nil +} diff --git a/convert/rio/option.go b/convert/rio/option.go new file mode 100644 index 00000000..5591cf6c --- /dev/null +++ b/convert/rio/option.go @@ -0,0 +1,66 @@ +package rio + +// Option configures a Converter option. +type Option func(*Converter) + +// WithDockerhub returns an option to set the default +// dockerhub registry connector. +func WithDockerhub(connector string) Option { + return func(d *Converter) { + d.dockerhubConn = connector + } +} + +// WithKubernetes returns an option to set the default +// runtime to Kubernetes. +func WithKubernetes(namespace, connector string) Option { + return func(d *Converter) { + d.kubeNamespace = namespace + d.kubeConnector = connector + } +} + +// WithIdentifier returns an option to set the pipeline +// identifier. +func WithIdentifier(identifier string) Option { + return func(d *Converter) { + d.pipelineId = identifier + } +} + +// WithName returns an option to set the pipeline name. +func WithName(name string) Option { + return func(d *Converter) { + d.pipelineName = name + } +} + +// WithOrganization returns an option to set the +// harness organization. +func WithOrganization(organization string) Option { + return func(d *Converter) { + d.pipelineOrg = organization + } +} + +// WithProject returns an option to set the harness +// project name. +func WithProject(project string) Option { + return func(d *Converter) { + d.pipelineProj = project + } +} + +// WithNotifyUserGroup returns an option to set the notification email. +func WithNotifyUserGroup(notifyUserGroup string) Option { + return func(d *Converter) { + d.notifyUserGroup = notifyUserGroup + } +} + +// WithGithubConnector returns an option to set the github connector. +func WithGithubConnector(githubConnector string) Option { + return func(d *Converter) { + d.githubConnector = githubConnector + } +} diff --git a/convert/rio/parse.go b/convert/rio/parse.go new file mode 100644 index 00000000..7891d867 --- /dev/null +++ b/convert/rio/parse.go @@ -0,0 +1 @@ +package rio diff --git a/convert/rio/yaml/parse.go b/convert/rio/yaml/parse.go new file mode 100644 index 00000000..30859c44 --- /dev/null +++ b/convert/rio/yaml/parse.go @@ -0,0 +1,41 @@ +package yaml + +import ( + "bytes" + "io" + "os" + + "gopkg.in/yaml.v3" +) + +// Parse parses the configuration from io.Reader r. +func Parse(r io.Reader) (*Config, error) { + out := new(Config) + dec := yaml.NewDecoder(r) + err := dec.Decode(out) + return out, err +} + +// ParseBytes parses the configuration from bytes b. +func ParseBytes(b []byte) (*Config, error) { + return Parse( + bytes.NewBuffer(b), + ) +} + +// ParseString parses the configuration from string s. +func ParseString(s string) (*Config, error) { + return ParseBytes( + []byte(s), + ) +} + +// ParseFile parses the configuration from path p. +func ParseFile(p string) (*Config, error) { + f, err := os.Open(p) + if err != nil { + return nil, err + } + defer f.Close() + return Parse(f) +} diff --git a/convert/rio/yaml/parse_test.go b/convert/rio/yaml/parse_test.go new file mode 100644 index 00000000..bb11fdba --- /dev/null +++ b/convert/rio/yaml/parse_test.go @@ -0,0 +1,21 @@ +package yaml + +import ( + "testing" +) + +func TestShort(t *testing.T) { + fileName := "testdata/short.yaml" + _, err := ParseFile(fileName) + if err != nil { + t.Fatal(err) + } +} + +func TestLong(t *testing.T) { + fileName := "testdata/long.yaml" + _, err := ParseFile(fileName) + if err != nil { + t.Fatal(err) + } +} diff --git a/convert/rio/yaml/pipeline.go b/convert/rio/yaml/pipeline.go new file mode 100644 index 00000000..1801ce9f --- /dev/null +++ b/convert/rio/yaml/pipeline.go @@ -0,0 +1,76 @@ +package yaml + +type ( + // Config defines a rio pipeline. + Config struct { + SchemaVersion float32 `yaml:"schemaVersion"` + Timeout int `yaml:"timeout,omitempty"` + Pipelines []Pipeline `yaml:"pipelines"` + Notify Notify `yaml:"notify,omitempty"` + } + + Pipeline struct { + Name string `yaml:"name,omitempty"` + BranchName string `yaml:"branchName,omitempty"` + Machine struct { + BaseImage string `yaml:"baseImage,omitempty"` + Env map[string]string `yaml:"env,omitempty"` + TargetPlatforms []string `yaml:"targetPlatforms,omitempty"` + } `yaml:"machine,omitempty"` + Build Build `yaml:"build,omitempty"` + Reports Reports `yaml:"reports,omitempty"` + Pkg Pkg `yaml:"package,omitempty"` + Finally Finally `yaml:"finally,omitempty"` + Trigger struct { + GitPush bool `yaml:"gitPush,omitempty"` + } `yaml:"trigger,omitempty"` + Checkout struct { + FetchTags bool `yaml:"fetchTags,omitempty"` + } `yaml:"checkout,omitempty"` + } + + Build struct { + Template string `yaml:"template,omitempty"` + Steps []string `yaml:"steps,omitempty"` + } + + Reports struct { + Findbugs bool `yaml:"findbugs,omitempty"` + Jacoco map[string]string `yaml:"jacoco,omitempty"` + } + + Pkg struct { + Release bool `yaml:"release,omitempty"` + Dockerfile []Dockerfile `yaml:"dockerfile,omitempty"` + } + + Dockerfile struct { + Context string `yaml:"context,omitempty"` + Version string `yaml:"version,omitempty"` + PerApplication bool `yaml:"perApplication,omitempty"` + DockerfilePath string `yaml:"dockerfilePath,omitempty"` + Env map[string]string `yaml:"env,omitempty"` + Publish []Publish `yaml:"publish,omitempty"` + ExtraTags []string `yaml:"extraTags,omitempty"` + } + + Publish struct { + Repo string `yaml:"repo,omitempty"` + } + + Finally struct { + Tag struct { + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"tag,omitempty"` + } + + Notify struct { + Email struct { + Enabled bool `yaml:"enabled,omitempty"` + } `yaml:"email,omitempty"` + PullRequestComment struct { + PostOnSuccess bool `yaml:"postOnSuccess,omitempty"` + PostOnFailure bool `yaml:"postOnFailure,omitempty"` + } `yaml:"pullRequestComment,omitempty"` + } +) diff --git a/convert/rio/yaml/testdata/long.yaml b/convert/rio/yaml/testdata/long.yaml new file mode 100644 index 00000000..13e43c6d --- /dev/null +++ b/convert/rio/yaml/testdata/long.yaml @@ -0,0 +1,641 @@ +schemaVersion: 2.0 +timeout: 30 + +pipelines: + - name: Harness-main-build-prb + branchName: main + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:prb + steps: + - ./ci_scripts/sample_script.sh + - ./mvnw clean package -Dmaven.test.skip=true= + reports: + findbugs: true + jacoco: + exec: '**/target/jacoco.exec' + src: '**/src/main/java' + classes: '**/target/classes' + + - name: Harness-main-build-publish-dev + branchName: main + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:publish + steps: + - ./mvnw clean package -Dmaven.test.skip=true + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./artifactoryservice + version: artifactoryservice + perApplication: false + dockerfilePath: ./artifactoryservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./healthcheckservice + version: healthcheckservice + perApplication: false + dockerfilePath: ./healthcheckservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./Test-Scorecard + version: ossfscorecard + perApplication: false + dockerfilePath: ./Test-Scorecard/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./scorecardservice + version: scorecardservice + perApplication: false + dockerfilePath: ./scorecardservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./vulnerabilityservice + version: vulnerabilityservice + perApplication: false + dockerfilePath: ./vulnerabilityservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./externalservice + version: externalservice + perApplication: false + dockerfilePath: ./externalservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./communicationservice + version: communicationservice + perApplication: false + dockerfilePath: ./communicationservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + - context: ./schedulerservice + version: schedulerservice + perApplication: false + dockerfilePath: ./schedulerservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev + + finally: + tag: + enabled: false + + - name: Harness-main-build-publish-develop + branchName: develop + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:publish + steps: + - ./mvnw clean package -Dmaven.test.skip=true + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./artifactoryservice + version: artifactoryservice + perApplication: false + dockerfilePath: ./artifactoryservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./healthcheckservice + version: healthcheckservice + perApplication: false + dockerfilePath: ./healthcheckservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./Test-Scorecard + version: ossfscorecard + perApplication: false + dockerfilePath: ./Test-Scorecard/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./scorecardservice + version: scorecardservice + perApplication: false + dockerfilePath: ./scorecardservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./vulnerabilityservice + version: vulnerabilityservice + perApplication: false + dockerfilePath: ./vulnerabilityservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./externalservice + version: externalservice + perApplication: false + dockerfilePath: ./externalservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./communicationservice + version: communicationservice + perApplication: false + dockerfilePath: ./communicationservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + - context: ./schedulerservice + version: schedulerservice + perApplication: false + dockerfilePath: ./schedulerservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-develop + + finally: + tag: + enabled: false + + + - name: Harness-main-build-publish-qa + branchName: main + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:publish + steps: + - ./mvnw clean package -Dmaven.test.skip=true + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./artifactoryservice + version: artifactoryservice + perApplication: false + dockerfilePath: ./artifactoryservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./healthcheckservice + version: healthcheckservice + perApplication: false + dockerfilePath: ./healthcheckservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./Test-Scorecard + version: ossfscorecard + perApplication: false + dockerfilePath: ./Test-Scorecard/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./scorecardservice + version: scorecardservice + perApplication: false + dockerfilePath: ./scorecardservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./vulnerabilityservice + version: vulnerabilityservice + perApplication: false + dockerfilePath: ./vulnerabilityservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./externalservice + version: externalservice + perApplication: false + dockerfilePath: ./externalservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./communicationservice + version: communicationservice + perApplication: false + dockerfilePath: ./communicationservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + - context: ./schedulerservice + version: schedulerservice + perApplication: false + dockerfilePath: ./schedulerservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-qa + + finally: + tag: + enabled: false + + - name: Harness-main-build-publish-testbranch + branchName: test + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:publish + steps: + - ./mvnw clean package -Dmaven.test.skip=true + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./artifactoryservice + version: artifactoryservice + perApplication: false + dockerfilePath: ./artifactoryservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./healthcheckservice + version: healthcheckservice + perApplication: false + dockerfilePath: ./healthcheckservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./Test-Scorecard + version: ossfscorecard + perApplication: false + dockerfilePath: ./Test-Scorecard/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./scorecardservice + version: scorecardservice + perApplication: false + dockerfilePath: ./scorecardservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./vulnerabilityservice + version: vulnerabilityservice + perApplication: false + dockerfilePath: ./vulnerabilityservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./externalservice + version: externalservice + perApplication: false + dockerfilePath: ./externalservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./communicationservice + version: communicationservice + perApplication: false + dockerfilePath: ./communicationservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + - context: ./schedulerservice + version: schedulerservice + perApplication: false + dockerfilePath: ./schedulerservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-test + + finally: + tag: + enabled: false + + + - name: Harness-main-build-publish-uat + branchName: main + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:publish + steps: + - ./mvnw clean package -Dmaven.test.skip=true + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./artifactoryservice + version: artifactoryservice + perApplication: false + dockerfilePath: ./artifactoryservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./healthcheckservice + version: healthcheckservice + perApplication: false + dockerfilePath: ./healthcheckservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./Test-Scorecard + version: ossfscorecard + perApplication: false + dockerfilePath: ./Test-Scorecard/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./scorecardservice + version: scorecardservice + perApplication: false + dockerfilePath: ./scorecardservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./vulnerabilityservice + version: vulnerabilityservice + perApplication: false + dockerfilePath: ./vulnerabilityservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./externalservice + version: externalservice + perApplication: false + dockerfilePath: ./externalservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./communicationservice + version: communicationservice + perApplication: false + dockerfilePath: ./communicationservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + - context: ./schedulerservice + version: schedulerservice + perApplication: false + dockerfilePath: ./schedulerservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uat + + finally: + tag: + enabled: false + + - name: Harness-main-build-publish-uatbranch + branchName: uat + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:publish + steps: + - ./mvnw clean package -Dmaven.test.skip=true + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./artifactoryservice + version: artifactoryservice + perApplication: false + dockerfilePath: ./artifactoryservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./healthcheckservice + version: healthcheckservice + perApplication: false + dockerfilePath: ./healthcheckservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./Test-Scorecard + version: ossfscorecard + perApplication: false + dockerfilePath: ./Test-Scorecard/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./scorecardservice + version: scorecardservice + perApplication: false + dockerfilePath: ./scorecardservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./vulnerabilityservice + version: vulnerabilityservice + perApplication: false + dockerfilePath: ./vulnerabilityservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./externalservice + version: externalservice + perApplication: false + dockerfilePath: ./externalservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./communicationservice + version: communicationservice + perApplication: false + dockerfilePath: ./communicationservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + - context: ./schedulerservice + version: schedulerservice + perApplication: false + dockerfilePath: ./schedulerservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-uatbranch + + finally: + tag: + enabled: false + + + - name: Harness-main-test-prb + branchName: main + machine: + baseImage: docker.harness.io/base-images/ubi8/java17-builder:latest + env: + RUNTIME_JDK_VERSION: 17 + build: + template: freestyle:v4:prb + steps: + - ./mvnw clean package -Dmaven.test.skip=true + reports: + findbugs: true + jacoco: + exec: '**/target/jacoco.exec' + src: '**/src/main/java' + classes: '**/target/classes' + package: + release: true + dockerfile: + - context: ./aggregatorservice + version: aggregatorservice + extraTags: ["0.1.0.-${RIO_BUILD_NUMBER}"] + perApplication: false + dockerfilePath: ./aggregatorservice/Dockerfile + env: + RIO_APP_DIR: . + publish: + - repo: docker.harness.io/cloudtech-security/harness-dev \ No newline at end of file diff --git a/convert/rio/yaml/testdata/short.yaml b/convert/rio/yaml/testdata/short.yaml new file mode 100644 index 00000000..f27daf0d --- /dev/null +++ b/convert/rio/yaml/testdata/short.yaml @@ -0,0 +1,60 @@ +schemaVersion: 2.0 + +notify: + email: + enabled: true + pullRequestComment: + postOnSuccess: true + postOnFailure: true + +package: + dockerfile: + perApplication: false + +pipelines: + - name: pull-request-build + branchName: main + machine: + baseImage: docker.harness.io/base-images/ubi9/go1.20-builder + build: + template: freestyle:v4:prb + steps: + - make test + - make build + + - name: pull-request-pre-commit + branchName: main + machine: + baseImage: docker.harness.io/cloud-technologies/build-tools-go:1.1.14 + env: + GOPRIVATE: "github.pie.harness.io" + build: + template: freestyle:v4:prb + steps: + - git config --global url."git@github.pie.harness.io:".insteadOf "https://github.pie.harness.io/" + - pre-commit install + - pre-commit run --all-files + + - name: publish + branchName: main + trigger: + gitPush: true + machine: + executor: + type: moes + targetPlatforms: + - linux/amd64 + - linux/arm64 + baseImage: docker.harness.io/base-images/ubi9/go1.20-builder + build: + template: freestyle:v4:publish + steps: + - make build + checkout: + fetchTags: true + package: + dockerfile: + - dockerfilePath: Dockerfile + context: bin + publish: + - repo: docker.harness.io/cloudtech/core-service-iam \ No newline at end of file diff --git a/main.go b/main.go index 49f88ea7..c152f73b 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,7 @@ func main() { subcommands.Register(new(command.Jenkins), "") subcommands.Register(new(command.Travis), "") subcommands.Register(new(command.Downgrade), "") + subcommands.Register(new(command.Rio), "") flag.Parse() ctx := context.Background()