Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
eyalbe4 committed Aug 20, 2024
2 parents 2f1cbb7 + d507532 commit 99fd0f6
Show file tree
Hide file tree
Showing 12 changed files with 388 additions and 86 deletions.
131 changes: 105 additions & 26 deletions artifactory/commands/commandssummaries/buildinfosummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
buildInfo "github.com/jfrog/build-info-go/entities"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container"
"github.com/jfrog/jfrog-cli-core/v2/commandsummary"
"path"
"strings"
Expand Down Expand Up @@ -59,22 +60,12 @@ func (bis *BuildInfoSummary) buildInfoTable(builds []*buildInfo.BuildInfo) strin

func (bis *BuildInfoSummary) buildInfoModules(builds []*buildInfo.BuildInfo) string {
var markdownBuilder strings.Builder
markdownBuilder.WriteString("\n\n ### Modules Published As Part of This Build \n\n")
markdownBuilder.WriteString("\n### Modules Published As Part of This Build\n")
var shouldGenerate bool
for _, build := range builds {
for _, module := range build.Modules {
if len(module.Artifacts) == 0 {
continue
}

switch module.Type {
case buildInfo.Docker, buildInfo.Maven, buildInfo.Npm, buildInfo.Go, buildInfo.Generic, buildInfo.Terraform:
markdownBuilder.WriteString(bis.generateModuleMarkdown(module))
shouldGenerate = true
default:
// Skip unsupported module types.
continue
}
if modulesMarkdown := bis.generateModulesMarkdown(build.Modules...); modulesMarkdown != "" {
markdownBuilder.WriteString(modulesMarkdown)
shouldGenerate = true
}
}

Expand All @@ -85,19 +76,47 @@ func (bis *BuildInfoSummary) buildInfoModules(builds []*buildInfo.BuildInfo) str
return markdownBuilder.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"
func (bis *BuildInfoSummary) generateModulesMarkdown(modules ...buildInfo.Module) string {
var modulesMarkdown strings.Builder
parentToModulesMap := groupModulesByParent(modules)
if len(parentToModulesMap) == 0 {
return ""
}
// Format the time in a more human-readable format and save it in a variable
return buildInfoTime.Format(timeFormat)

for parentModuleID, parentModules := range parentToModulesMap {
modulesMarkdown.WriteString(fmt.Sprintf("#### %s\n<pre>", parentModuleID))
isMultiModule := len(parentModules) > 1

for _, module := range parentModules {
if isMultiModule && parentModuleID == module.Id {
// Skip the parent module if there are multiple modules, as it will be displayed as a header
continue
}
modulesMarkdown.WriteString(bis.generateModuleArtifactsTree(&module, isMultiModule))
}
modulesMarkdown.WriteString("</pre>\n")
}
return modulesMarkdown.String()
}

func (bis *BuildInfoSummary) generateModuleMarkdown(module buildInfo.Module) string {
var moduleMarkdown strings.Builder
moduleMarkdown.WriteString(fmt.Sprintf("\n #### %s \n", module.Id))
func (bis *BuildInfoSummary) generateModuleArtifactsTree(module *buildInfo.Module, shouldCollapseArtifactsTree bool) string {
artifactsTree := bis.createArtifactsTree(module)
if shouldCollapseArtifactsTree {
return bis.generateModuleCollapsibleSection(module, artifactsTree)
}
return artifactsTree
}

func (bis *BuildInfoSummary) generateModuleCollapsibleSection(module *buildInfo.Module, sectionContent string) string {
switch module.Type {
case buildInfo.Docker:
return createCollapsibleSection(createDockerMultiArchTitle(module, bis.platformUrl), sectionContent)
default:
return createCollapsibleSection(module.Id, sectionContent)
}
}

func (bis *BuildInfoSummary) createArtifactsTree(module *buildInfo.Module) string {
artifactsTree := utils.NewFileTree()
for _, artifact := range module.Artifacts {
artifactUrlInArtifactory := bis.generateArtifactUrl(artifact)
Expand All @@ -108,8 +127,7 @@ func (bis *BuildInfoSummary) generateModuleMarkdown(module buildInfo.Module) str
artifactTreePath := path.Join(artifact.OriginalDeploymentRepo, artifact.Path)
artifactsTree.AddFile(artifactTreePath, artifactUrlInArtifactory)
}
moduleMarkdown.WriteString("\n\n <pre>" + artifactsTree.String() + "</pre>")
return moduleMarkdown.String()
return artifactsTree.String()
}

func (bis *BuildInfoSummary) generateArtifactUrl(artifact buildInfo.Artifact) string {
Expand All @@ -118,3 +136,64 @@ func (bis *BuildInfoSummary) generateArtifactUrl(artifact buildInfo.Artifact) st
}
return generateArtifactUrl(bis.platformUrl, path.Join(artifact.OriginalDeploymentRepo, artifact.Path), bis.majorVersion)
}

// groupModulesByParent groups modules that share the same parent ID into a map where the key is the parent ID and the value is a slice of those modules.
func groupModulesByParent(modules []buildInfo.Module) map[string][]buildInfo.Module {
parentToModulesMap := make(map[string][]buildInfo.Module, len(modules))
for _, module := range modules {
if len(module.Artifacts) == 0 || !isSupportedModule(&module) {
continue
}

parentID := module.Parent
// If the module has no parent, that means it is the parent module itself, so we can use its ID as the parent ID.
if parentID == "" {
parentID = module.Id
}
parentToModulesMap[parentID] = append(parentToModulesMap[parentID], module)
}
return parentToModulesMap
}

func isSupportedModule(module *buildInfo.Module) bool {
switch module.Type {
case buildInfo.Maven, buildInfo.Npm, buildInfo.Go, buildInfo.Generic, buildInfo.Terraform:
return true
case buildInfo.Docker:
// Skip attestations that are added as a module for multi-arch docker builds
return !strings.HasPrefix(module.Id, container.AttestationsModuleIdPrefix)
default:
return false
}
}

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)
}

func createDockerMultiArchTitle(module *buildInfo.Module, platformUrl string) string {
// Extract the parent image name from the module ID (e.g. my-image:1.0 -> my-image)
parentImageName := strings.Split(module.Parent, ":")[0]

// Get the relevant SHA256
var sha256 string
for _, artifact := range module.Artifacts {
if artifact.Name == container.ManifestJsonFile {
sha256 = artifact.Sha256
break
}
}
// Create a link to the Docker package in Artifactory UI
dockerModuleLink := fmt.Sprintf(artifactoryDockerPackagesUiFormat, strings.TrimSuffix(platformUrl, "/"), "%2F%2F"+parentImageName, sha256)
return fmt.Sprintf("%s <a href=%s>(🐸 View)</a>", module.Id, dockerModuleLink)
}

func createCollapsibleSection(title, content string) string {
return fmt.Sprintf("<details><summary>%s</summary>\n%s</details>", title, content)
}
Loading

0 comments on commit 99fd0f6

Please sign in to comment.