Skip to content

Commit

Permalink
Command Summaries (jfrog#1179)
Browse files Browse the repository at this point in the history
  • Loading branch information
EyalDelarea authored May 22, 2024
1 parent a62576f commit 44e989e
Show file tree
Hide file tree
Showing 11 changed files with 656 additions and 20 deletions.
18 changes: 18 additions & 0 deletions artifactory/commands/buildinfo/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package buildinfo
import (
"errors"
"fmt"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/commandssummaries"
"github.com/jfrog/jfrog-cli-core/v2/commandsummary"
"net/url"
"strconv"
"strings"
Expand Down Expand Up @@ -139,6 +141,10 @@ func (bpc *BuildPublishCommand) Run() error {
return err
}

if err = recordCommandSummary(buildInfo, buildLink); err != nil {
return err
}

logMsg := "Build info successfully deployed."
if bpc.IsDetailedSummary() {
log.Info(logMsg + " Browse it in Artifactory under " + buildLink)
Expand Down Expand Up @@ -229,3 +235,15 @@ func (bpc *BuildPublishCommand) getNextBuildNumber(buildName string, servicesMan
latestBuildNumber++
return strconv.Itoa(latestBuildNumber), nil
}

func recordCommandSummary(buildInfo *buildinfo.BuildInfo, buildLink string) (err error) {
if !commandsummary.ShouldRecordSummary() {
return
}
buildInfo.BuildUrl = buildLink
buildInfoSummary, err := commandsummary.New(commandssummaries.NewBuildInfo(), "build-info")
if err != nil {
return
}
return buildInfoSummary.Record(buildInfo)
}
57 changes: 57 additions & 0 deletions artifactory/commands/commandssummaries/buildinfosummary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package commandssummaries

import (
"fmt"
buildInfo "github.com/jfrog/build-info-go/entities"
"github.com/jfrog/jfrog-cli-core/v2/commandsummary"
"strings"
"time"
)

const timeFormat = "Jan 2, 2006 , 15:04:05"

type BuildInfoSummary struct{}

func NewBuildInfo() *BuildInfoSummary {
return &BuildInfoSummary{}
}

func (bis *BuildInfoSummary) GenerateMarkdownFromFiles(dataFilePaths []string) (finalMarkdown string, err error) {
// Aggregate all the build info files into a slice
var builds []*buildInfo.BuildInfo
for _, path := range dataFilePaths {
var publishBuildInfo buildInfo.BuildInfo
if err = commandsummary.UnmarshalFromFilePath(path, &publishBuildInfo); err != nil {
return
}
builds = append(builds, &publishBuildInfo)
}

if len(builds) > 0 {
finalMarkdown = bis.buildInfoTable(builds)
}
return
}

func (bis *BuildInfoSummary) buildInfoTable(builds []*buildInfo.BuildInfo) string {
// Generate a string that represents a Markdown table
var tableBuilder strings.Builder
tableBuilder.WriteString("\n\n| Build Info | Time Stamp | \n")
tableBuilder.WriteString("|---------|------------| \n")
for _, build := range builds {
buildTime := parseBuildTime(build.Started)
tableBuilder.WriteString(fmt.Sprintf("| [%s](%s) | %s |\n", build.Name+" "+build.Number, build.BuildUrl, buildTime))
}
tableBuilder.WriteString("\n\n")
return tableBuilder.String()
}

func parseBuildTime(timestamp string) string {
// Parse the timestamp string into a time.Time object
buildInfoTime, err := time.Parse(buildInfo.TimeFormat, timestamp)
if err != nil {
return "N/A"
}
// Format the time in a more human-readable format and save it in a variable
return buildInfoTime.Format(timeFormat)
}
32 changes: 32 additions & 0 deletions artifactory/commands/commandssummaries/buildinfosummary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package commandssummaries

import (
buildinfo "github.com/jfrog/build-info-go/entities"
"github.com/stretchr/testify/assert"
"testing"
)

func TestBuildInfoTable(t *testing.T) {
gh := &BuildInfoSummary{}
var builds = []*buildinfo.BuildInfo{
{
Name: "buildName",
Number: "123",
Started: "2024-05-05T12:47:20.803+0300",
BuildUrl: "http://myJFrogPlatform/builds/buildName/123",
},
}
expected := "\n\n| Build Info | Time Stamp | \n|---------|------------| \n| [buildName 123](http://myJFrogPlatform/builds/buildName/123) | May 5, 2024 , 12:47:20 |\n\n\n"
assert.Equal(t, expected, gh.buildInfoTable(builds))
}

func TestParseBuildTime(t *testing.T) {
// Test format
actual := parseBuildTime("2006-01-02T15:04:05.000-0700")
expected := "Jan 2, 2006 , 15:04:05"
assert.Equal(t, expected, actual)
// Test invalid format
expected = "N/A"
actual = parseBuildTime("")
assert.Equal(t, expected, actual)
}
66 changes: 66 additions & 0 deletions artifactory/commands/commandssummaries/uploadsummary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package commandssummaries

import (
"fmt"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/commandsummary"
)

type UploadSummary struct {
uploadTree *utils.FileTree
uploadedArtifacts ResultsWrapper
PlatformUrl string
JfrogProjectKey string
}

type UploadResult struct {
SourcePath string `json:"sourcePath"`
TargetPath string `json:"targetPath"`
RtUrl string `json:"rtUrl"`
}

type ResultsWrapper struct {
Results []UploadResult `json:"results"`
}

func NewUploadSummary(platformUrl, projectKey string) *UploadSummary {
return &UploadSummary{
PlatformUrl: platformUrl,
JfrogProjectKey: projectKey,
}
}

func (us *UploadSummary) GenerateMarkdownFromFiles(dataFilePaths []string) (markdown string, err error) {
if err = us.loadResults(dataFilePaths); err != nil {
return
}
// Wrap the markdown in a <pre> tags to preserve spaces
markdown = fmt.Sprintf("\n<pre>\n\n\n" + us.generateFileTreeMarkdown() + "</pre>\n\n")
return
}

// Loads all the recorded results from the given file paths.
func (us *UploadSummary) loadResults(filePaths []string) error {
us.uploadedArtifacts = ResultsWrapper{}
for _, path := range filePaths {
var uploadResult ResultsWrapper
if err := commandsummary.UnmarshalFromFilePath(path, &uploadResult); err != nil {
return err
}
us.uploadedArtifacts.Results = append(us.uploadedArtifacts.Results, uploadResult.Results...)
}
return nil
}

func (us *UploadSummary) generateFileTreeMarkdown() string {
us.uploadTree = utils.NewFileTree()
for _, uploadResult := range us.uploadedArtifacts.Results {
us.uploadTree.AddFile(uploadResult.TargetPath, us.buildUiUrl(uploadResult.TargetPath))
}
return us.uploadTree.String()
}

func (us *UploadSummary) buildUiUrl(targetPath string) string {
template := "%sui/repos/tree/General/%s/?projectKey=%s"
return fmt.Sprintf(template, us.PlatformUrl, targetPath, us.JfrogProjectKey)
}
24 changes: 24 additions & 0 deletions artifactory/commands/commandssummaries/uploadsummary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package commandssummaries

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestBuildUiUrl(t *testing.T) {
gh := &UploadSummary{
PlatformUrl: "https://myplatform.com/",
JfrogProjectKey: "myProject",
}
expected := "https://myplatform.com/ui/repos/tree/General/myPath/?projectKey=myProject"
actual := gh.buildUiUrl("myPath")
assert.Equal(t, expected, actual)

gh = &UploadSummary{
PlatformUrl: "https://myplatform.com/",
JfrogProjectKey: "",
}
expected = "https://myplatform.com/ui/repos/tree/General/myPath/?projectKey="
actual = gh.buildUiUrl("myPath")
assert.Equal(t, expected, actual)
}
44 changes: 44 additions & 0 deletions artifactory/commands/generic/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package generic

import (
"errors"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/commandssummaries"
"github.com/jfrog/jfrog-cli-core/v2/commandsummary"
"os"

buildInfo "github.com/jfrog/build-info-go/entities"

Expand Down Expand Up @@ -153,6 +156,10 @@ func (uc *UploadCommand) upload() (err error) {
}
successCount = summary.TotalSucceeded
failCount = summary.TotalFailed

if err = recordCommandSummary(summary, serverDetails.Url, uc.buildConfiguration); err != nil {
return
}
}
} else {
successCount, failCount, err = servicesManager.UploadFiles(uploadParamsArray...)
Expand Down Expand Up @@ -188,6 +195,7 @@ func (uc *UploadCommand) upload() (err error) {
}
return build.PopulateBuildArtifactsAsPartials(buildArtifacts, uc.buildConfiguration, buildInfo.Generic)
}

return
}

Expand Down Expand Up @@ -271,3 +279,39 @@ func createDeleteSpecForSync(deletePattern string, syncDeletesProp string) *spec
Recursive(true).
BuildSpec()
}

func recordCommandSummary(summary *rtServicesUtils.OperationSummary, platformUrl string, buildConfig *build.BuildConfiguration) (err error) {
if !commandsummary.ShouldRecordSummary() {
return
}
// Get project key if exists
var projectKey string
if buildConfig != nil {
projectKey = buildConfig.GetProject()
}
uploadSummary, err := commandsummary.New(commandssummaries.NewUploadSummary(platformUrl, projectKey), "upload")
if err != nil {
return
}
data, err := readDetailsFromReader(summary.TransferDetailsReader)
if err != nil {
return
}
return uploadSummary.Record(data)
}

// Reads transfer details from the reader and return the content as bytes for further processing
func readDetailsFromReader(reader *content.ContentReader) (readContent []byte, err error) {
if reader == nil {
return
}
for _, file := range reader.GetFilesPaths() {
// Read source file
sourceBytes, err := os.ReadFile(file)
if err != nil {
return nil, errorutils.CheckError(err)
}
readContent = append(readContent, sourceBytes...)
}
return
}
Loading

0 comments on commit 44e989e

Please sign in to comment.