diff --git a/cmd/convert.go b/cmd/convert.go index d4a67a2..0e0ee0c 100644 --- a/cmd/convert.go +++ b/cmd/convert.go @@ -1,11 +1,13 @@ package cmd import ( + "bytes" "github.com/linuxsuren/github-action-workflow/pkg" "github.com/spf13/cobra" "golang.org/x/exp/maps" "gopkg.in/yaml.v2" "os" + "path/filepath" "strings" ) @@ -26,14 +28,50 @@ func newConvertCmd() (c *cobra.Command) { } func (o *convertOption) runE(cmd *cobra.Command, args []string) (err error) { - gh := &pkg.Workflow{} - var data []byte - if data, err = os.ReadFile(args[0]); err == nil { - if err = yaml.Unmarshal(data, gh); err != nil { + var result string + if result, err = o.convertWorkflowsFromFilePath(args[0]); err == nil { + cmd.Println(result) + } + return +} + +func (o *convertOption) convertWorkflowsFromFilePath(targetPath string) (result string, err error) { + var ghs []*pkg.Workflow + var files []string + if files, err = filepath.Glob(targetPath); err == nil { + for _, file := range files { + var data []byte + if data, err = os.ReadFile(file); err == nil { + gh := &pkg.Workflow{} + if err = yaml.Unmarshal(data, gh); err == nil { + ghs = append(ghs, gh) + continue + } + } return } + result, err = o.convertWorkflows(ghs) } + return +} +func (o *convertOption) convertWorkflows(ghs []*pkg.Workflow) (output string, err error) { + buf := bytes.Buffer{} + + var result string + for i := range ghs { + if result, err = o.convert(ghs[i]); err != nil { + return + } + + buf.WriteString("\n---\n") + buf.WriteString(strings.TrimSpace(result)) + } + output = buf.String() + return +} + +func (o *convertOption) convert(gh *pkg.Workflow) (output string, err error) { for i, job := range gh.Jobs { for j, step := range job.Steps { if step.Env == nil { @@ -44,10 +82,7 @@ func (o *convertOption) runE(cmd *cobra.Command, args []string) (err error) { } } - var result string - if result, err = gh.ConvertToArgoWorkflow(); err == nil { - cmd.Println(strings.TrimSpace(result)) - } + output, err = gh.ConvertToArgoWorkflow() return } diff --git a/cmd/convert_test.go b/cmd/convert_test.go new file mode 100644 index 0000000..6b9a319 --- /dev/null +++ b/cmd/convert_test.go @@ -0,0 +1,261 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/linuxsuren/github-action-workflow/pkg" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "os" + "testing" +) + +func TestCmdConvert(t *testing.T) { + command := newConvertCmd() + assert.Equal(t, "convert", command.Use) +} + +func Test_convertOption_convert(t *testing.T) { + type fields struct { + env map[string]string + } + type args struct { + gh *pkg.Workflow + } + tests := []struct { + name string + fields fields + args args + wantOutput string + wantErr bool + }{{ + name: "simple", + args: args{ + gh: &pkg.Workflow{ + Name: "simple", + Jobs: map[string]pkg.Job{ + "simple": { + Name: "test", + Steps: []pkg.Step{{ + Name: "test", + Run: "echo 1", + }}, + }, + }}, + }, + wantOutput: "data/one-workflow.yaml", + }, { + name: "workflow with env", + args: args{ + gh: &pkg.Workflow{ + Name: "simple", + Jobs: map[string]pkg.Job{ + "simple": { + Name: "test", + Steps: []pkg.Step{{ + Name: "test", + Run: "echo 1", + Env: map[string]string{}, + }}, + }, + }}, + }, + fields: fields{env: map[string]string{"key": "value"}}, + wantOutput: "data/one-workflow-env.yaml", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &convertOption{ + env: tt.fields.env, + } + gotOutput, err := o.convert(tt.args.gh) + if (err != nil) != tt.wantErr { + t.Errorf("convert() error = %v, wantErr %v", err, tt.wantErr) + return + } + if data, err := os.ReadFile(tt.wantOutput); err == nil { + tt.wantOutput = string(data) + } + assert.Equal(t, tt.wantOutput, gotOutput) + }) + } +} + +func Test_convertOption_convertWorkflows(t *testing.T) { + type fields struct { + env map[string]string + } + type args struct { + ghs []*pkg.Workflow + } + tests := []struct { + name string + fields fields + args args + wantOutput string + wantErr assert.ErrorAssertionFunc + }{{ + name: "two workflows", + args: args{ + ghs: []*pkg.Workflow{{ + Name: "simple", + Jobs: map[string]pkg.Job{ + "simple": { + Name: "test", + Steps: []pkg.Step{{ + Name: "test", + Run: "echo 1", + Env: map[string]string{}, + }}, + }, + }}, { + Name: "simple", + Jobs: map[string]pkg.Job{ + "simple": { + Name: "test", + Steps: []pkg.Step{{ + Name: "test", + Run: "echo 1", + Env: map[string]string{}, + }}, + }, + }}}, + }, + wantOutput: "data/combine-workflow.yaml", + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return true + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &convertOption{ + env: tt.fields.env, + } + gotOutput, err := o.convertWorkflows(tt.args.ghs) + if !tt.wantErr(t, err, fmt.Sprintf("convertWorkflows(%v)", tt.args.ghs)) { + return + } + if data, err := os.ReadFile(tt.wantOutput); err == nil { + tt.wantOutput = string(data) + } + assert.Equal(t, tt.wantOutput, gotOutput) + }) + } +} + +func Test_convertOption_convertWorkflowsFromFilePath(t *testing.T) { + type fields struct { + env map[string]string + } + type args struct { + targetPath string + } + tests := []struct { + name string + fields fields + args args + wantResult string + wantErr assert.ErrorAssertionFunc + }{{ + name: "one github workflow", + args: args{targetPath: "data/github-workflow-*.yaml"}, + wantResult: simpleWorkflow, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + assert.Nil(t, err) + return true + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &convertOption{ + env: tt.fields.env, + } + gotResult, err := o.convertWorkflowsFromFilePath(tt.args.targetPath) + if !tt.wantErr(t, err, fmt.Sprintf("convertWorkflowsFromFilePath(%v)", tt.args.targetPath)) { + return + } + assert.Equalf(t, tt.wantResult, gotResult, "convertWorkflowsFromFilePath(%v)", tt.args.targetPath) + }) + } +} + +const simpleWorkflow = ` +--- +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: build +spec: + entrypoint: main + volumeClaimTemplates: + - metadata: + name: work + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 64Mi + + templates: + - name: main + dag: + tasks: + - name: test + template: test + - name: test + script: + image: alpine + command: [sh] + source: | + go test ./... -coverprofile coverage.out + volumeMounts: + - mountPath: /work + name: work + workingDir: /work` + +func Test_convertOption_runE(t *testing.T) { + type fields struct { + env map[string]string + } + type args struct { + cmd *cobra.Command + args []string + } + tests := []struct { + name string + fields fields + args args + prepare func(c *cobra.Command) *bytes.Buffer + check func(t assert.TestingT, buf *bytes.Buffer) + wantErr assert.ErrorAssertionFunc + }{{ + name: "simple", + args: args{ + cmd: &cobra.Command{}, + args: []string{"data/github-workflow-*.yaml"}, + }, + prepare: func(c *cobra.Command) *bytes.Buffer { + buf := bytes.Buffer{} + c.SetOut(&buf) + return &buf + }, + check: func(t assert.TestingT, buf *bytes.Buffer) { + assert.Equal(t, simpleWorkflow+"\n", buf.String()) + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + assert.Nil(t, err) + return true + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := &convertOption{ + env: tt.fields.env, + } + buf := tt.prepare(tt.args.cmd) + err := o.runE(tt.args.cmd, tt.args.args) + tt.check(t, buf) + tt.wantErr(t, err, fmt.Sprintf("runE(%v, %v)", tt.args.cmd, tt.args.args)) + }) + } +} diff --git a/cmd/data/combine-workflow.yaml b/cmd/data/combine-workflow.yaml new file mode 100644 index 0000000..40844ff --- /dev/null +++ b/cmd/data/combine-workflow.yaml @@ -0,0 +1,65 @@ + +--- +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: simple +spec: + entrypoint: main + volumeClaimTemplates: + - metadata: + name: work + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 64Mi + + templates: + - name: main + dag: + tasks: + - name: test + template: test + - name: test + script: + image: alpine + command: [sh] + source: | + echo 1 + volumeMounts: + - mountPath: /work + name: work + workingDir: /work +--- +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: simple +spec: + entrypoint: main + volumeClaimTemplates: + - metadata: + name: work + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 64Mi + + templates: + - name: main + dag: + tasks: + - name: test + template: test + - name: test + script: + image: alpine + command: [sh] + source: | + echo 1 + volumeMounts: + - mountPath: /work + name: work + workingDir: /work \ No newline at end of file diff --git a/cmd/data/github-workflow-simple.yaml b/cmd/data/github-workflow-simple.yaml new file mode 100644 index 0000000..6a16066 --- /dev/null +++ b/cmd/data/github-workflow-simple.yaml @@ -0,0 +1,10 @@ +name: Build + +jobs: + build: + name: Build + runs-on: ubuntu-20.04 + steps: + - name: Test + run: | + go test ./... -coverprofile coverage.out \ No newline at end of file diff --git a/cmd/data/one-workflow-env.yaml b/cmd/data/one-workflow-env.yaml new file mode 100644 index 0000000..fdf6663 --- /dev/null +++ b/cmd/data/one-workflow-env.yaml @@ -0,0 +1,34 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: simple +spec: + entrypoint: main + volumeClaimTemplates: + - metadata: + name: work + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 64Mi + + templates: + - name: main + dag: + tasks: + - name: test + template: test + - name: test + script: + image: alpine + command: [sh] + env: + - name: key + value: value + source: | + echo 1 + volumeMounts: + - mountPath: /work + name: work + workingDir: /work \ No newline at end of file diff --git a/cmd/data/one-workflow.yaml b/cmd/data/one-workflow.yaml new file mode 100644 index 0000000..7e9a97b --- /dev/null +++ b/cmd/data/one-workflow.yaml @@ -0,0 +1,31 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: simple +spec: + entrypoint: main + volumeClaimTemplates: + - metadata: + name: work + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 64Mi + + templates: + - name: main + dag: + tasks: + - name: test + template: test + - name: test + script: + image: alpine + command: [sh] + source: | + echo 1 + volumeMounts: + - mountPath: /work + name: work + workingDir: /work \ No newline at end of file diff --git a/cmd/root_test.go b/cmd/root_test.go new file mode 100644 index 0000000..9bcb695 --- /dev/null +++ b/cmd/root_test.go @@ -0,0 +1,11 @@ +package cmd + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestCmdMeta(t *testing.T) { + command := NewRoot() + assert.Equal(t, "gaw", command.Use) +}