Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to print information about newly upgraded Jenkins #593

Merged
merged 15 commits into from
Sep 3, 2021
210 changes: 210 additions & 0 deletions app/cmd/center_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package cmd

import (
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)

//LtsURL is the URL of stable Jenkins RSS
const LtsURL = "https://www.jenkins.io/changelog-stable/rss.xml"

//WidthOfDescription is the width of the description column
const WidthOfDescription = 60

//ASCIIOfLineFeed is the ASCII of line feed
const ASCIIOfLineFeed = 10

//ASCIIOfSpace is the ASCII of space
const ASCIIOfSpace = 32

//CenterListOption as options for Jenkins RSS
type CenterListOption struct {
Channel Channel `xml:"channel"`
// RoundTripper http.RoundTripper
}

//Channel as part of CenterListOption
type Channel struct {
Title string `xml:"title"`
Items []Item `xml:"item"`
}

//Item as a option for information of newly-released Jenkins
type Item struct {
Title string `xml:"title"`
Description string `xml:"description"`
PubDate string `xml:"pubDate"`
}

var numberOfLines int
var coreVersion string

func init() {
centerCmd.AddCommand(centerListCmd)
centerListCmd.Flags().IntVar(&numberOfLines, "amount", 10,
i18n.T("the amount of information to be printed in description column"))
centerListCmd.Flags().StringVarP(&coreVersion, "version", "v", "",
i18n.T("current jenkins version"))
}

var centerListCmd = &cobra.Command{
Use: "list",
Short: i18n.T("Print the information of recent-released Jenkins"),
Long: i18n.T("Print the information of recent-released Jenkins which are newer than the installed one."),
Example: `jcli center list --version 2.345.1
jcli center list`,
PreRunE: checkConnectionWithJenkins,
RunE: func(cmd *cobra.Command, _ []string) (err error) {
var jenkinsVersion string
if coreVersion != "" {
jenkinsVersion = coreVersion
changeLog, err := getChangelog(LtsURL, jenkinsVersion, numberOfLines, getVersionData)
cmd.Println(changeLog)
return err
}
return err
},
}

func checkConnectionWithJenkins(cmd *cobra.Command, args []string) (err error) {
if coreVersion != "" {
matched, _ := regexp.MatchString("^[0-9]*\\.[0-9]*\\.[0-9]*", coreVersion)
if matched {
return nil
}
panic("Format of the jenkins version is not correct. The format should be sth like 2.235.2")
} else if coreVersion == "" {
jCoreClient := &client.JenkinsStatusClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: pluginFormulaOption.RoundTripper,
},
}
getCurrentJenkinsAndClient(&(jCoreClient.JenkinsCore))
var status *client.JenkinsStatus
status, err := jCoreClient.Get()
if status.Version == "" {
err = fmt.Errorf("cannot get the version of current Jenkins, error is %v. Please check current status of your jenkins and your .jenkins-cli.yaml", err)
panic(err)
}
coreVersion = status.Version
}
return err
}

func getVersionData(rss string) ([]Item, string, error) {
resp, err := http.Get(rss)
if err != nil {
return nil, "", err
}
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, "", err
}
var centerListOption CenterListOption
xml.Unmarshal(bytes, &centerListOption)
resp.Body.Close()
return centerListOption.Channel.Items, centerListOption.Channel.Title, nil
}

func getChangelog(rss string, version string, lines int, getData func(rss string) ([]Item, string, error)) (changelog string, err error) {
items, title, error := getData(rss)
if error != nil {
return "", error
}
t := table.NewWriter()
rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
t.AppendHeader(table.Row{title, title, title, title}, rowConfigAutoMerge)
t.AppendRow(table.Row{"Index", "Title", "Description", "PubDate"})
var temp string
var isTheLatestVersion = 1
for index, item := range items {
if compareVersionTitle(version, item.Title[8:]) >= 0 {
break
}
isTheLatestVersion = 0
temp = strings.Replace(item.Description, "\n", "", -1)
temp = trimXMLSymbols(temp)
temp = regulateWidthAndLines(temp, WidthOfDescription, lines)
t.AppendRow([]interface{}{index + 1, item.Title, temp, item.PubDate[:17]})
t.AppendSeparator()
}
if isTheLatestVersion == 1 {
return "You already have the latest version of Jenkins installed!", nil
}
return t.Render(), nil
}
func regulateWidthAndLines(content string, width int, numberOfLines int) string {
var count = 0
myContent := []uint8(content)
var i int
for i = 0; i < len(myContent); i++ {
if myContent[i] == ASCIIOfLineFeed {
count++
}
if count == numberOfLines {
break
}
}
myContent = myContent[:i]
indexInLine := 0
for index, char := range myContent {
indexInLine++
if indexInLine%width == 0 {
if char == ASCIIOfSpace {
myContent[index] = ASCIIOfLineFeed
indexInLine = 0
} else {
indexInLine = 0
for ; myContent[index] != ASCIIOfSpace; index-- {
indexInLine++
}
myContent[index] = ASCIIOfLineFeed
}
}
if char == ASCIIOfLineFeed {
indexInLine = 0
}
}
return string(myContent)
}

func compareVersionTitle(versionOne string, versionTwo string) int {
versionOneString := strings.Split(versionOne, ".")
versionTwoString := strings.Split(versionTwo, ".")
if strings.Compare(versionOne, versionTwo) == 0 {
return 0
}
if versionOneString[0] > versionTwoString[0] {
return 1
} else if versionOneString[0] < versionTwoString[0] {
return -1
}
if versionOneString[1] != "" && versionTwoString[1] != "" && versionOneString[1] > versionTwoString[1] {
return 1
} else if versionOneString[1] < versionTwoString[1] {
return -1
}
if versionOneString[2] != "" && versionTwoString[2] != "" && versionOneString[2] > versionTwoString[2] {
return 1
}
return -1
}

func trimXMLSymbols(temp string) string {
temp = strings.TrimSpace(temp)
xmlSymbols := []string{"<br>", "<br/>", "<br />", "<ul>", "<li>", "</ul>", "<strong>", "</strong>", "<code>", "</code>", "<em>", "</em>"}
for _, xmlSymbol := range xmlSymbols {
temp = strings.Replace(temp, xmlSymbol, "", -1)
}
temp = strings.Replace(temp, "</li>", "\n", -1)
return temp
}
9 changes: 9 additions & 0 deletions app/cmd/center_list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
+-----------------------------------------------------------------------------------------------------------------------------------------+
| JENKINS LTS CHANGELOG |
+-------------------------+-------------------------+-----------------------------------------------------------+-------------------------+
| Index | Title | Description | PubDate |
| 1 | Jenkins 2.289.2 | Security: Important security fixes. | Wed, 30 Jun 2021 |
| | | RFE: Winstone 5.18: Update Jetty from 9.4.39.v20210325 to | |
| | | 9.4.41.v20210516 for bug fixes and enhancements. | |
| | | | |
+-------------------------+-------------------------+-----------------------------------------------------------+-------------------------+
108 changes: 108 additions & 0 deletions app/cmd/center_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package cmd

import (
"bytes"
_ "embed"
"encoding/xml"
"io/ioutil"
"net/http"
"os"

"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

//go:embed center_list.txt
var resultOneVersionData string

var _ = Describe("center list command", func() {
var (
ctrl *gomock.Controller
// roundTripper *mhttp.MockRoundTripper
)

BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
// roundTripper = mhttp.NewMockRoundTripper(ctrl)
// centerListOption.RoundTripper = roundTripper
rootCmd.SetArgs([]string{})
rootOptions.Jenkins = ""
rootOptions.ConfigFile = "test.yaml"

})

AfterEach(func() {
rootCmd.SetArgs([]string{})
os.Remove(rootOptions.ConfigFile)
rootOptions.ConfigFile = ""
ctrl.Finish()
})

Context("basic cases", func() {
jenkinsVersions := []string{"Jenkins 2.289.2", "Jenkins 2.289.1"}
result := []string{
"You already have the latest version of Jenkins installed!",
resultOneVersionData,
}
It("no jenkins version information in the list", func() {
data, err := GenerateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
temp, _ := getChangelog(LtsURL, jenkinsVersions[0][8:], 10, mockGetVersionData)
Expect(temp).To(Equal(result[0]))
})

It("one jenkins version information in the list", func() {
data, err := GenerateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
temp, _ := getChangelog(LtsURL, jenkinsVersions[1][8:], 10, mockGetVersionData)
Expect(temp).To(Equal(result[1]))
})
})
})

func mockGetVersionData(rss string) ([]Item, string, error) {
requestVersionData, _ := http.NewRequest(http.MethodGet, rss, nil)
responseVersionData := &http.Response{
StatusCode: 200,
Proto: "HTTP/1.1",
Request: requestVersionData,
Body: ioutil.NopCloser(bytes.NewBufferString(versionXML())),
}
bytes, err := ioutil.ReadAll(responseVersionData.Body)
if err != nil {
return nil, "", err
}
var centerListOption CenterListOption
xml.Unmarshal(bytes, &centerListOption)
return centerListOption.Channel.Items, centerListOption.Channel.Title, nil
}

func versionXML() string {
return `<rss>
<channel>
<title> Jenkins LTS Changelog </title>
<item>
<title>Jenkins 2.289.2</title>
<description>&lt;strong&gt;&lt;/strong&gt;&lt;ul&gt;&lt;li&gt; Security: Important security fixes. &lt;/li&gt;
&lt;li&gt;RFE: Winstone 5.18: Update Jetty from 9.4.39.v20210325 to 9.4.41.v20210516 for bug fixes and enhancements. &lt;/li&gt;
&lt;/ul&gt; </description>
<pubDate> Wed, 30 Jun 2021 00:00:00 +0000 </pubDate>
</item>
<item>
<title>Jenkins 2.289.1</title>
<description> &lt;strong&gt;Changes since 2.289:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt; Bug: Fix form submission for some specific form validation cases (regression in 2.289). &lt;/li&gt;
&lt;li&gt; Bug: Wrap the build name in the build results list if it is too long. &lt;/li&gt;
&lt;/ul&gt; </description>
<pubDate> Wed, 2 Jun 2021 00:00:00 +0000 </pubDate>
</item>
</channel>
</rss>`
}
9 changes: 5 additions & 4 deletions app/cmd/job_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package cmd

import (
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/cmd/common"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
"net/http"
"strconv"
"strings"
"time"

"github.com/jenkins-zh/jenkins-cli/app/cmd/common"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)

// JobLogOption is the job log option
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/google/go-github/v29 v29.0.3
github.com/hashicorp/go-version v1.2.1
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
github.com/jedib0t/go-pretty/v6 v6.2.4
github.com/jenkins-zh/jenkins-client v0.0.1
github.com/jenkins-zh/jenkins-formulas v0.0.5
github.com/linuxsuren/cobra-extension v0.0.10
Expand Down
Loading