diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml
index 34fc36c3a6630e3222677c0780997e47bdb1ee54..0a08c8ece31ff9c7ac4416af47a04a6aac4ab05a 100644
--- a/.github/workflows/pull-request.yaml
+++ b/.github/workflows/pull-request.yaml
@@ -8,7 +8,7 @@ on:
jobs:
build:
name: Build
- runs-on: ubuntu-latest
+ runs-on: ubuntu-18.04
steps:
- name: Set up Go 1.13
diff --git a/.gitignore b/.gitignore
index 28b3b97f241be5272a3d724d58d0ec2c2391c865..e20856a7ad69e33d5ffeb0deebe8225b69e56d27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
bin/
release/
+jcli
*.xml
diff --git a/Makefile b/Makefile
index f094e37d3c5aa3cd99a9c89c9d6c6979bfa26879..17d2aaf874662d4b6540b96fa67701d804a61403 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ init: gen-mock
darwin: gen-data
GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) GOOS=darwin GOARCH=amd64 $(GO) $(BUILD_TARGET) $(BUILDFLAGS) -o bin/darwin/$(NAME) $(MAIN_SRC_FILE)
chmod +x bin/darwin/$(NAME)
+ rm -rf jcli && ln -s bin/darwin/$(NAME) jcli
linux: gen-data
CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 $(GO) $(BUILD_TARGET) $(BUILDFLAGS) -o bin/linux/$(NAME) $(MAIN_SRC_FILE)
diff --git a/app/cmd/cwp.go b/app/cmd/cwp.go
new file mode 100644
index 0000000000000000000000000000000000000000..9c53ad1c56c435b89db016b23a712188c1969cde
--- /dev/null
+++ b/app/cmd/cwp.go
@@ -0,0 +1,192 @@
+package cmd
+
+import (
+ "encoding/xml"
+ "fmt"
+ "github.com/jenkins-zh/jenkins-cli/app/i18n"
+ "github.com/jenkins-zh/jenkins-cli/util"
+ "github.com/mitchellh/go-homedir"
+ "github.com/spf13/cobra"
+ "io/ioutil"
+ "os"
+ "path"
+)
+
+func init() {
+ rootCmd.AddCommand(cwpCmd)
+
+ cwpCmd.Flags().BoolVarP(&cwpOptions.BatchMode, "batch-mode", "", false,
+ i18n.T("Enables the batch mode for the build"))
+ cwpCmd.Flags().StringVarP(&cwpOptions.BomPath, "bom-path", "", "",
+ i18n.T("Path to the BOM file. If defined, it will override settings in Config YAML"))
+ cwpCmd.Flags().StringVarP(&cwpOptions.Environment, "environment", "", "",
+ i18n.T("Environment to be used"))
+ cwpCmd.Flags().BoolVarP(&cwpOptions.InstallArtifacts, "install-artifacts", "", false,
+ i18n.T("If set, the final artifacts will be automatically installed to the local repository (current version - only WAR)"))
+ cwpCmd.Flags().StringVarP(&cwpOptions.ConfigPath, "config-path", "", "",
+ i18n.T("Path to the configuration YAML. See the tool's README for format"))
+ cwpCmd.Flags().BoolVarP(&cwpOptions.Demo, "demo", "", false,
+ i18n.T("Enables demo mode with predefined config file"))
+ cwpCmd.Flags().StringVarP(&cwpOptions.MvnSettingsFile, "mvn-settings-file", "", "",
+ i18n.T("Path to a custom Maven settings file to be used within the build"))
+ cwpCmd.Flags().StringVarP(&cwpOptions.TmpDir, "tmp-dir", "", "",
+ i18n.T("Temporary directory for generated files and the output WAR."))
+ cwpCmd.Flags().StringVarP(&cwpOptions.Version, "version", "", "1.0-SNAPSHOT",
+ i18n.T("Version of WAR to be set."))
+
+ cwpCmd.Flags().BoolVarP(&cwpOptions.ShowProgress, "show-progress", "", true,
+ i18n.T("Show the progress of downloading files"))
+ cwpCmd.Flags().StringVarP(&cwpOptions.MetadataURL, "metadata-url", "",
+ "https://repo.jenkins-ci.org/list/releases/io/jenkins/tools/custom-war-packager/custom-war-packager-cli/maven-metadata.xml",
+ i18n.T("The metadata URL"))
+
+ localCache := path.Join(os.TempDir(), "/", ".jenkins-cli")
+ if userHome, err := homedir.Dir(); err == nil {
+ localCache = path.Join(userHome, "/", ".jenkins-cli")
+ }
+ cwpCmd.Flags().StringVarP(&cwpOptions.LocalCache, "local-cache", "", localCache,
+ i18n.T("The local cache directory"))
+}
+
+// CWPOptions is the option of custom-war-packager
+// see also https://github.com/jenkinsci/custom-war-packager
+type CWPOptions struct {
+ CommonOption
+
+ ConfigPath string
+ Version string
+ TmpDir string
+ Environment string
+ BomPath string
+ MvnSettingsFile string
+
+ BatchMode bool
+ Demo bool
+ InstallArtifacts bool
+
+ ShowProgress bool
+ MetadataURL string
+ LocalCache string
+}
+
+var cwpOptions CWPOptions
+
+var cwpCmd = &cobra.Command{
+ Use: "cwp",
+ Short: i18n.T("Custom Jenkins WAR packager for Jenkins"),
+ Long: i18n.T(`Custom Jenkins WAR packager for Jenkins
+This's a wrapper of https://github.com/jenkinsci/custom-war-packager`),
+ RunE: cwpOptions.Run,
+ Annotations: map[string]string{
+ since: "v0.0.27",
+ },
+}
+
+// Run is the main logic of cwp cmd
+func (o *CWPOptions) Run(cmd *cobra.Command, args []string) (err error) {
+ localCWP := o.getLocalCWP()
+ _, err = os.Stat(localCWP)
+ if os.IsNotExist(err) {
+ if err = o.Download(); err != nil {
+ return
+ }
+ } else if err != nil {
+ return
+ }
+
+ var binary string
+ binary, err = util.LookPath("java", o.LookPathContext)
+ if err == nil {
+ env := os.Environ()
+
+ cwpArgs := []string{"java"}
+ cwpArgs = append(cwpArgs, "-jar", localCWP)
+
+ if o.Demo {
+ cwpArgs = append(cwpArgs, "-demo")
+ }
+
+ if o.BatchMode {
+ cwpArgs = append(cwpArgs, "--batch-mode")
+ }
+
+ if o.InstallArtifacts {
+ cwpArgs = append(cwpArgs, "--installArtifacts")
+ }
+
+ if o.ConfigPath != "" {
+ cwpArgs = append(cwpArgs, "-configPath", o.ConfigPath)
+ }
+
+ if o.TmpDir != "" {
+ cwpArgs = append(cwpArgs, "-tmpDir", o.TmpDir)
+ }
+ err = util.Exec(binary, cwpArgs, env, o.SystemCallExec)
+ }
+ return
+}
+
+// Download get the latest cwp from server into local
+func (o *CWPOptions) Download() (err error) {
+ var latest string
+ if latest, err = o.GetLatest(); err == nil {
+ cwpURL := o.GetCWPURL(latest)
+
+ err = o.downloadFile(cwpURL, o.getLocalCWP())
+ }
+ return
+}
+
+// GetCWPURL returns the download URL of a specific version cwp
+func (o *CWPOptions) GetCWPURL(version string) string {
+ return fmt.Sprintf("https://repo.jenkins-ci.org/list/releases/io/jenkins/tools/custom-war-packager/custom-war-packager-cli/%s/custom-war-packager-cli-%s-jar-with-dependencies.jar",
+ version, version)
+}
+
+func (o *CWPOptions) getLocalCWP() string {
+ return path.Join(o.LocalCache, "cwp-cli.jar")
+}
+
+// GetLatest returns the latest of cwp
+func (o *CWPOptions) GetLatest() (version string, err error) {
+ metadataURL := o.MetadataURL
+ output := "metadata.xml"
+
+ if err = o.downloadFile(metadataURL, output); err == nil {
+ var data []byte
+
+ mavenMeta := MavenMetadata{}
+ if data, err = ioutil.ReadFile(output); err == nil {
+ err = xml.Unmarshal(data, &mavenMeta)
+ }
+
+ if err == nil {
+ version = mavenMeta.Versioning.Latest
+ }
+ }
+ return
+}
+
+func (o *CWPOptions) downloadFile(url, output string) (err error) {
+ downloader := util.HTTPDownloader{
+ RoundTripper: o.RoundTripper,
+ TargetFilePath: output,
+ URL: url,
+ ShowProgress: o.ShowProgress,
+ }
+ err = downloader.DownloadFile()
+ return
+}
+
+// MavenMetadata is the maven metadata xml root
+type MavenMetadata struct {
+ XMLName xml.Name `xml:"metadata"`
+ Versioning MavenVersioning `xml:"versioning"`
+}
+
+// MavenVersioning is the versioning of maven
+type MavenVersioning struct {
+ XMLName xml.Name `xml:"versioning"`
+ Latest string `xml:"latest"`
+ Release string `xml:"release"`
+}
diff --git a/app/cmd/cwp_test.go b/app/cmd/cwp_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..cca2795fcc2d2a4b2a9e30240a82cece66f478c2
--- /dev/null
+++ b/app/cmd/cwp_test.go
@@ -0,0 +1,133 @@
+package cmd
+
+import (
+ "bytes"
+ "github.com/golang/mock/gomock"
+ "github.com/jenkins-zh/jenkins-cli/mock/mhttp"
+ "github.com/jenkins-zh/jenkins-cli/util"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/stretchr/testify/assert"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path"
+ "testing"
+)
+
+var _ = Describe("cwp command test", func() {
+ var (
+ ctrl *gomock.Controller
+ localCache string
+ )
+
+ BeforeEach(func() {
+ ctrl = gomock.NewController(GinkgoT())
+ localCache = os.TempDir()
+ roundTripper := mhttp.NewMockRoundTripper(ctrl)
+ cwpOptions = CWPOptions{
+ CommonOption: CommonOption{RoundTripper: roundTripper},
+ MetadataURL: "http://localhost/maven-metadata.xml",
+ LocalCache: localCache,
+ }
+ prepareMavenMetadataRequest(roundTripper)
+
+ fakeContent := "hello"
+ prepareDownloadFileRequest(cwpOptions.GetCWPURL("2.0-alpha-2"), fakeContent, roundTripper)
+
+ cwpOptions.SystemCallExec = util.FakeSystemCallExecSuccess
+ cwpOptions.LookPathContext = util.FakeLookPath
+ })
+
+ AfterEach(func() {
+ os.RemoveAll(localCache)
+ ctrl.Finish()
+ })
+
+ Context("basic test", func() {
+ It("should success", func() {
+ rootCmd.SetArgs([]string{"cwp"})
+ _, err := rootCmd.ExecuteC()
+ Expect(err).NotTo(HaveOccurred())
+ })
+ })
+})
+
+func TestDownload(t *testing.T) {
+ ctrl := gomock.NewController(t)
+
+ tmpDir := os.TempDir()
+ defer os.RemoveAll(tmpDir)
+
+ roundTripper := mhttp.NewMockRoundTripper(ctrl)
+ cwpOpts := CWPOptions{
+ CommonOption: CommonOption{RoundTripper: roundTripper},
+ MetadataURL: "http://localhost/maven-metadata.xml",
+ LocalCache: tmpDir,
+ }
+ prepareMavenMetadataRequest(roundTripper)
+
+ fakeContent := "hello"
+ prepareDownloadFileRequest(cwpOpts.GetCWPURL("2.0-alpha-2"), fakeContent, roundTripper)
+
+ err := cwpOpts.Download()
+ assert.Nil(t, err)
+
+ var data []byte
+ data, err = ioutil.ReadFile(path.Join(tmpDir, "cwp-cli.jar"))
+ assert.Nil(t, err)
+ assert.Equal(t, fakeContent, string(data))
+}
+
+func TestGetLatest(t *testing.T) {
+ ctrl := gomock.NewController(t)
+
+ roundTripper := mhttp.NewMockRoundTripper(ctrl)
+ cwpOpts := CWPOptions{
+ CommonOption: CommonOption{RoundTripper: roundTripper},
+ MetadataURL: "http://localhost/maven-metadata.xml",
+ }
+ prepareMavenMetadataRequest(roundTripper)
+
+ ver, err := cwpOpts.GetLatest()
+ assert.Nil(t, err)
+ assert.Equal(t, "2.0-alpha-2", ver)
+}
+
+func prepareDownloadFileRequest(url, content string, roundTripper *mhttp.MockRoundTripper) {
+ request, _ := http.NewRequest("GET", url, nil)
+ response := &http.Response{
+ StatusCode: 200,
+ Request: request,
+ Body: ioutil.NopCloser(bytes.NewBufferString(content)),
+ }
+ roundTripper.EXPECT().
+ RoundTrip(request).Return(response, nil)
+}
+
+func prepareMavenMetadataRequest(roundTripper *mhttp.MockRoundTripper) {
+ request, _ := http.NewRequest("GET", "http://localhost/maven-metadata.xml", nil)
+ response := &http.Response{
+ StatusCode: 200,
+ Request: request,
+ Body: ioutil.NopCloser(bytes.NewBufferString(getMavenMetadataSample())),
+ }
+ roundTripper.EXPECT().
+ RoundTrip(request).Return(response, nil)
+}
+
+func getMavenMetadataSample() string {
+ return `
+
+ io.jenkins.tools.custom-war-packager
+ custom-war-packager-cli
+
+ 2.0-alpha-2
+ 2.0-alpha-2
+
+ 2.0-alpha-2
+
+ 20190815083928
+
+`
+}
diff --git a/app/cmd/doc.go b/app/cmd/doc.go
index 674e4a4f2be4b5a942ee625787340f18879038c3..27722b39fee7c2bcfeaacf9c57f5281a977ebe8c 100644
--- a/app/cmd/doc.go
+++ b/app/cmd/doc.go
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
+ "os"
"path"
"path/filepath"
"strings"
@@ -28,10 +29,11 @@ version: %s
)
var docCmd = &cobra.Command{
- Use: "doc