From cb6d3d576521f42d483737244cfdc80ca42ecc20 Mon Sep 17 00:00:00 2001 From: ccwxl Date: Tue, 5 Aug 2025 14:41:26 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(deploy):=20=E6=B7=BB=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=20Maven=20=E5=91=BD=E4=BB=A4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 mvnCmd 字符串变量,用于存储自定义 Maven 命令 - 在帮助文档中添加 Scenario 1,说明如何使用自定义 Maven 命令进行构建和部署 - 修改 execMavenBuild 函数,支持使用自定义 Maven 命令 - 在 init 函数中添加 mvnCmd 的命令行参数定义 --- v1/cmd/deploy/deploy.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/v1/cmd/deploy/deploy.go b/v1/cmd/deploy/deploy.go index 050aa70..2af93bb 100644 --- a/v1/cmd/deploy/deploy.go +++ b/v1/cmd/deploy/deploy.go @@ -51,6 +51,8 @@ var ( podFlag string podNamespace string // pre parsed pod namespace podName string // pre parsed pod name + + mvnCmd string //custom maven command ) const ( @@ -71,16 +73,19 @@ We advice you to use this in local dev phase. Scenario 0: Build bundle at current workspace and deploy it to a local running ark container with default port: arkctl deploy -Scenario 1: Build bundle at given path and deploy it to local running ark container with given port: +Scenario 1: Build the bundle using a custom Maven command to compile the project in the current workspace and deploy it to a locally running Ark container on the default port.: + arkctl deploy --mvnCmd ${mvnCmd} + +Scenario 2: Build bundle at given path and deploy it to local running ark container with given port: arkctl deploy --port ${your ark container portFlag} ${path/to/your/project} -Scenario 2: Deploy a local pre-built bundle to local running ark container: +Scenario 3: Deploy a local pre-built bundle to local running ark container: arkctl deploy ${path/to/your/pre/built/bundle.jar} -Scenario 3: Build and deploy a bundle at current dir to a remote running ark container in k8s cluster with default port: +Scenario 4: Build and deploy a bundle at current dir to a remote running ark container in k8s cluster with default port: arkctl deploy --pod ${namespace}/${name} -Scenario 4: Build an maven multi module project and deploy a sub module to a running ark container: +Scenario 5: Build an maven multi module project and deploy a sub module to a running ark container: arkctl deploy --sub ${path/to/your/sub/module} `, Args: func(cmd *cobra.Command, args []string) error { @@ -113,11 +118,20 @@ func execMavenBuild(ctx *contextutil.Context) bool { style.InfoPrefix("Stage").Println("BuildBundle") style.InfoPrefix("BuildDirectory").Println(defaultArg) + compileCmd := "mvn" + compileArg := []string{"clean", "package", "-Dmaven.test.skip=true"} + if mvnCmd != "" { + // custom maven command + args := append(compileArg, strings.Split(mvnCmd, " ")...) + compileCmd = args[0] + compileArg = args[1:] + } + mvn := cmdutil.BuildCommandWithWorkDir( ctx, defaultArg, - "mvn", - "clean", "package", "-Dmaven.test.skip=true") + compileCmd, + compileArg...) style.InfoPrefix("Command").Println(mvn.String()) if err := mvn.Exec(); err != nil { @@ -450,6 +464,11 @@ func init() { DeployCommand.Flags().StringVar(&podFlag, "pod", "", ` If Provided, arkctl will try to deploy the bundle to the ark container running in given pod. `) + + DeployCommand.Flags().StringVar(&mvnCmd, "mvnCmd", "", ` +If Provided, arkctl will try to deploy the mvnCmd to compile maven project. +`) + DeployCommand.Flags().StringVar(&subBundlePath, "sub", "", ` If Provided, arkctl will try to build the project at current dir and deploy the bundle at subBundlePath. `) From e8fee9b774ccf7cfd23e3fa5f412b99c62a44ed2 Mon Sep 17 00:00:00 2001 From: ccwxl Date: Tue, 5 Aug 2025 14:47:09 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix(v1):=20=E4=BF=AE=E5=A4=8D=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=20Maven=20=E5=91=BD=E4=BB=A4=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复了自定义 Maven 命令的参数解析逻辑 - 现在可以正确处理空格分隔的 Maven 命令参数 --- v1/cmd/deploy/deploy.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/v1/cmd/deploy/deploy.go b/v1/cmd/deploy/deploy.go index 2af93bb..98c9d55 100644 --- a/v1/cmd/deploy/deploy.go +++ b/v1/cmd/deploy/deploy.go @@ -122,9 +122,11 @@ func execMavenBuild(ctx *contextutil.Context) bool { compileArg := []string{"clean", "package", "-Dmaven.test.skip=true"} if mvnCmd != "" { // custom maven command - args := append(compileArg, strings.Split(mvnCmd, " ")...) - compileCmd = args[0] - compileArg = args[1:] + args := strings.Split(mvnCmd, " ") + if len(args) > 0 { + compileCmd = args[0] + compileArg = args[1:] + } } mvn := cmdutil.BuildCommandWithWorkDir( From f98910da7afd2f01a132b6327bc8fe792ec60110 Mon Sep 17 00:00:00 2001 From: ccwxl Date: Thu, 21 Aug 2025 15:05:41 +0800 Subject: [PATCH 3/5] =?UTF-8?q?refactor(v1):=20=E9=87=8D=E6=9E=84=20Maven?= =?UTF-8?q?=20=E5=91=BD=E4=BB=A4=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 Maven命令解析逻辑提取到单独的函数 parseMavenCommand 中- 在 deploy_test.go 中添加 parseMavenCommand 函数的单元测试 - 优化了命令行参数的分割方式,使用 strings.Fields替代 strings.Split --- v1/cmd/deploy/deploy.go | 24 +++++++----- v1/cmd/deploy/deploy_test.go | 71 ++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 v1/cmd/deploy/deploy_test.go diff --git a/v1/cmd/deploy/deploy.go b/v1/cmd/deploy/deploy.go index 98c9d55..5e3a7be 100644 --- a/v1/cmd/deploy/deploy.go +++ b/v1/cmd/deploy/deploy.go @@ -118,16 +118,7 @@ func execMavenBuild(ctx *contextutil.Context) bool { style.InfoPrefix("Stage").Println("BuildBundle") style.InfoPrefix("BuildDirectory").Println(defaultArg) - compileCmd := "mvn" - compileArg := []string{"clean", "package", "-Dmaven.test.skip=true"} - if mvnCmd != "" { - // custom maven command - args := strings.Split(mvnCmd, " ") - if len(args) > 0 { - compileCmd = args[0] - compileArg = args[1:] - } - } + compileCmd, compileArg := parseMavenCommand(mvnCmd) mvn := cmdutil.BuildCommandWithWorkDir( ctx, @@ -167,6 +158,19 @@ func execMavenBuild(ctx *contextutil.Context) bool { return true } +func parseMavenCommand(mvnCmd string) (string, []string) { + compileCmd := "mvn" + compileArg := []string{"clean", "package", "-Dmaven.test.skip=true"} + if mvnCmd != "" { + args := strings.Fields(mvnCmd) + if len(args) > 0 { + compileCmd = args[0] + compileArg = args[1:] + } + } + return compileCmd, compileArg +} + func execParseBizModel(ctx *contextutil.Context) bool { style.InfoPrefix("Stage").Println("ParseBizModel") bundlePath := osutil.GetLocalFileProtocol() + defaultArg diff --git a/v1/cmd/deploy/deploy_test.go b/v1/cmd/deploy/deploy_test.go new file mode 100644 index 0000000..3611511 --- /dev/null +++ b/v1/cmd/deploy/deploy_test.go @@ -0,0 +1,71 @@ +/** + * 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 deploy + +import ( + "reflect" + "testing" +) + +func TestParseMavenCommand(t *testing.T) { + tests := []struct { + name string + mvnCmd string + wantCmd string + wantArgs []string + }{ + { + name: "empty command", + mvnCmd: "", + wantCmd: "mvn", + wantArgs: []string{"clean", "package", "-Dmaven.test.skip=true"}, + }, + { + name: "custom command without args", + mvnCmd: "mvnw", + wantCmd: "mvnw", + wantArgs: []string{}, + }, + { + name: "custom command with args", + mvnCmd: "mvn clean install", + wantCmd: "mvn", + wantArgs: []string{"clean", "install"}, + }, + { + name: "complex command with args", + mvnCmd: "mvnw clean package -DskipTests", + wantCmd: "mvnw", + wantArgs: []string{"clean", "package", "-DskipTests"}, + }, + { + name: "command with multiple spaces", + mvnCmd: "mvn clean package", + wantCmd: "mvn", + wantArgs: []string{"clean", "package"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotCmd, gotArgs := parseMavenCommand(tt.mvnCmd) + if gotCmd != tt.wantCmd { + t.Errorf("parseMavenCommand() gotCmd = %v, want %v", gotCmd, tt.wantCmd) + } + if !reflect.DeepEqual(gotArgs, tt.wantArgs) { + t.Errorf("parseMavenCommand() gotArgs = %v, want %v", gotArgs, tt.wantArgs) + } + }) + } +} From e21756662f640b75cbd76015edf9ae02c8c20764 Mon Sep 17 00:00:00 2001 From: ccwxl Date: Wed, 27 Aug 2025 15:22:51 +0800 Subject: [PATCH 4/5] fix --- go.mod | 3 +- go.sum | 2 ++ v1/cmd/deploy/deploy.go | 12 +++++-- v1/cmd/deploy/deploy_test.go | 61 ++++++++++++++++++++++++++++++++---- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 179b3fb..ce21f01 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.21.3 require ( github.com/go-resty/resty/v2 v2.11.0 + github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.4.0 github.com/magiconair/properties v1.8.5 github.com/manifoldco/promptui v0.9.0 @@ -15,6 +16,7 @@ require ( github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.8.4 golang.org/x/net v0.23.0 + golang.org/x/text v0.14.0 ) require ( @@ -44,7 +46,6 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 82c6fd5..89872ac 100644 --- a/go.sum +++ b/go.sum @@ -123,6 +123,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= diff --git a/v1/cmd/deploy/deploy.go b/v1/cmd/deploy/deploy.go index 5e3a7be..18964a8 100644 --- a/v1/cmd/deploy/deploy.go +++ b/v1/cmd/deploy/deploy.go @@ -53,6 +53,10 @@ var ( podName string // pre parsed pod name mvnCmd string //custom maven command + + // default maven compile command and args, centralized to avoid drift with tests + defaultMavenCmd = "mvn" + defaultMavenArgs = []string{"clean", "package", "-Dmaven.test.skip=true"} ) const ( @@ -159,8 +163,8 @@ func execMavenBuild(ctx *contextutil.Context) bool { } func parseMavenCommand(mvnCmd string) (string, []string) { - compileCmd := "mvn" - compileArg := []string{"clean", "package", "-Dmaven.test.skip=true"} + compileCmd := defaultMavenCmd + compileArg := append([]string(nil), defaultMavenArgs...) if mvnCmd != "" { args := strings.Fields(mvnCmd) if len(args) > 0 { @@ -472,7 +476,9 @@ If Provided, arkctl will try to deploy the bundle to the ark container running i `) DeployCommand.Flags().StringVar(&mvnCmd, "mvnCmd", "", ` -If Provided, arkctl will try to deploy the mvnCmd to compile maven project. +If provided, overrides the Maven invocation for building. Example: "mvn -q -T1C" or "./mvnw clean package". +Tokens are split by whitespace (strings.Fields); shell-style quoting is not supported via this single flag. +When empty, defaults to: mvn `+strings.Join(defaultMavenArgs, " ")+`. `) DeployCommand.Flags().StringVar(&subBundlePath, "sub", "", ` diff --git a/v1/cmd/deploy/deploy_test.go b/v1/cmd/deploy/deploy_test.go index 3611511..4624d04 100644 --- a/v1/cmd/deploy/deploy_test.go +++ b/v1/cmd/deploy/deploy_test.go @@ -14,11 +14,16 @@ package deploy import ( - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestParseMavenCommand(t *testing.T) { + t.Parallel() + + defaultArgs := append([]string(nil), defaultMavenArgs...) + tests := []struct { name string mvnCmd string @@ -28,8 +33,8 @@ func TestParseMavenCommand(t *testing.T) { { name: "empty command", mvnCmd: "", - wantCmd: "mvn", - wantArgs: []string{"clean", "package", "-Dmaven.test.skip=true"}, + wantCmd: defaultMavenCmd, + wantArgs: defaultArgs, }, { name: "custom command without args", @@ -55,16 +60,60 @@ func TestParseMavenCommand(t *testing.T) { wantCmd: "mvn", wantArgs: []string{"clean", "package"}, }, + { + name: "whitespace-only command", + mvnCmd: " \t\n ", + wantCmd: defaultMavenCmd, + wantArgs: defaultArgs, + }, + { + name: "leading and trailing spaces", + mvnCmd: " mvn clean package ", + wantCmd: "mvn", + wantArgs: []string{"clean", "package"}, + }, + { + name: "tabs between tokens", + mvnCmd: "mvn\tclean\tinstall", + wantCmd: "mvn", + wantArgs: []string{"clean", "install"}, + }, + { + name: "windows wrapper with flags", + mvnCmd: "mvnw.cmd -q -T1C", + wantCmd: "mvnw.cmd", + wantArgs: []string{"-q", "-T1C"}, + }, + { + name: "absolute path mvn", + mvnCmd: "/usr/local/bin/mvn -q", + wantCmd: "/usr/local/bin/mvn", + wantArgs: []string{"-q"}, + }, + { + name: "relative path wrapper", + mvnCmd: "./mvnw -q", + wantCmd: "./mvnw", + wantArgs: []string{"-q"}, + }, + { + name: "args with equals and profile", + mvnCmd: "mvn -DskipTests=true -Pprod", + wantCmd: "mvn", + wantArgs: []string{"-DskipTests=true", "-Pprod"}, + }, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() gotCmd, gotArgs := parseMavenCommand(tt.mvnCmd) if gotCmd != tt.wantCmd { - t.Errorf("parseMavenCommand() gotCmd = %v, want %v", gotCmd, tt.wantCmd) + t.Errorf("parseMavenCommand() gotCmd = %q, want %q", gotCmd, tt.wantCmd) } - if !reflect.DeepEqual(gotArgs, tt.wantArgs) { - t.Errorf("parseMavenCommand() gotArgs = %v, want %v", gotArgs, tt.wantArgs) + if diff := cmp.Diff(tt.wantArgs, gotArgs); diff != "" { + t.Errorf("parseMavenCommand() args mismatch (-want +got):\n%s", diff) } }) } From 1666fcdcf3c78e91238a055a8b1bc672b80477df Mon Sep 17 00:00:00 2001 From: ccwxl Date: Thu, 28 Aug 2025 16:41:35 +0800 Subject: [PATCH 5/5] fix --- v1/cmd/deploy/deploy.go | 1 + v1/cmd/deploy/deploy_test.go | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/v1/cmd/deploy/deploy.go b/v1/cmd/deploy/deploy.go index 18964a8..20743ff 100644 --- a/v1/cmd/deploy/deploy.go +++ b/v1/cmd/deploy/deploy.go @@ -479,6 +479,7 @@ If Provided, arkctl will try to deploy the bundle to the ark container running i If provided, overrides the Maven invocation for building. Example: "mvn -q -T1C" or "./mvnw clean package". Tokens are split by whitespace (strings.Fields); shell-style quoting is not supported via this single flag. When empty, defaults to: mvn `+strings.Join(defaultMavenArgs, " ")+`. +Note: providing any non-empty value replaces the default arguments entirely. Supplying just "mvn" or "mvnw" yields no default goals. `) DeployCommand.Flags().StringVar(&subBundlePath, "sub", "", ` diff --git a/v1/cmd/deploy/deploy_test.go b/v1/cmd/deploy/deploy_test.go index 4624d04..02ea843 100644 --- a/v1/cmd/deploy/deploy_test.go +++ b/v1/cmd/deploy/deploy_test.go @@ -17,6 +17,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" ) func TestParseMavenCommand(t *testing.T) { @@ -102,6 +103,24 @@ func TestParseMavenCommand(t *testing.T) { wantCmd: "mvn", wantArgs: []string{"-DskipTests=true", "-Pprod"}, }, + { + name: "thread-count with separate value", + mvnCmd: "mvn -T 1C", + wantCmd: "mvn", + wantArgs: []string{"-T", "1C"}, + }, + { + name: "multiple profiles in one flag", + mvnCmd: "mvn -Pprod,dev", + wantCmd: "mvn", + wantArgs: []string{"-Pprod,dev"}, + }, + { + name: "reactor and project list", + mvnCmd: "mvn -am -pl :module", + wantCmd: "mvn", + wantArgs: []string{"-am", "-pl", ":module"}, + }, } for _, tt := range tests { @@ -112,7 +131,7 @@ func TestParseMavenCommand(t *testing.T) { if gotCmd != tt.wantCmd { t.Errorf("parseMavenCommand() gotCmd = %q, want %q", gotCmd, tt.wantCmd) } - if diff := cmp.Diff(tt.wantArgs, gotArgs); diff != "" { + if diff := cmp.Diff(tt.wantArgs, gotArgs, cmpopts.EquateEmpty()); diff != "" { t.Errorf("parseMavenCommand() args mismatch (-want +got):\n%s", diff) } })