Skip to content

Commit

Permalink
Committer reviewer for rbv2
Browse files Browse the repository at this point in the history
  • Loading branch information
lesnerd committed Feb 23, 2025
1 parent 373315d commit f8a53ad
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 37 deletions.
2 changes: 2 additions & 0 deletions artifactory/utils/vcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"errors"
"fmt"
buildinfo "github.com/jfrog/build-info-go/entities"
gofrogcmd "github.com/jfrog/gofrog/io"
utils2 "github.com/jfrog/jfrog-cli-artifactory/evidence/utils"
Expand Down Expand Up @@ -163,6 +164,7 @@ func getMatchingRevisionFromBuild(buildInfo *buildinfo.BuildInfo, vcsUrl string)
break
}
}
log.Info(fmt.Sprintf("base commit: %s for build name: %s and build number: %s", lastVcsRevision, buildInfo.Name, buildInfo.Number))
return lastVcsRevision
}

Expand Down
5 changes: 5 additions & 0 deletions evidence/cli/command_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ func validateFoundSubjects(ctx *components.Context, foundSubjects []string) erro
return nil
}

if slices.Contains(foundSubjects, typeFlag) && slices.Contains(foundSubjects, releaseBundle) {
return nil
}

if slices.Contains(foundSubjects, typeFlag) && attemptSetBuildNameAndNumber(ctx) {
return nil
}
Expand Down Expand Up @@ -192,6 +196,7 @@ func platformToEvidenceUrls(rtDetails *coreConfig.ServerDetails) {
rtDetails.ArtifactoryUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "artifactory/"
rtDetails.EvidenceUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "evidence/"
rtDetails.MetadataUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "metadata/"
rtDetails.LifecycleUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "lifecycle/"
}

func assertValueProvided(c *components.Context, fieldName string) error {
Expand Down
18 changes: 14 additions & 4 deletions evidence/cli/command_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,23 @@ func (ebc *evidenceGitHubCommand) CreateEvidence(ctx *components.Context, server
ebc.ctx.GetStringFlagValue(project),
ebc.ctx.GetStringFlagValue(buildName),
ebc.ctx.GetStringFlagValue(buildNumber),
ebc.ctx.GetStringFlagValue(typeFlag))
ebc.ctx.GetStringFlagValue(typeFlag),
ebc.ctx.GetStringFlagValue(releaseBundle),
ebc.ctx.GetStringFlagValue(releaseBundleVersion))
return ebc.execute(createCmd)
}

func (ebc *evidenceGitHubCommand) validateEvidenceBuildContext(ctx *components.Context) error {
if !ctx.IsFlagSet(buildNumber) || assertValueProvided(ctx, buildNumber) != nil {
return errorutils.CheckErrorf("--%s is a mandatory field for creating a Release Bundle evidence", buildNumber)
if !ctx.IsFlagSet(typeFlag) {
return errorutils.CheckErrorf("--%s is a mandatory field for creating a GitHub evidence", typeFlag)
}
return nil
if ctx.IsFlagSet(buildName) && assertValueProvided(ctx, buildName) == nil {
return nil
}
if ctx.IsFlagSet(releaseBundle) && assertValueProvided(ctx, releaseBundle) == nil &&
ctx.IsFlagSet(releaseBundleVersion) && assertValueProvided(ctx, releaseBundleVersion) == nil {
return nil
}
return errorutils.CheckErrorf("Either --build-name or --release-bundle and --release-bundle-version " +
"are mandatory fields for creating a GitHub evidence")
}
180 changes: 162 additions & 18 deletions evidence/create_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import (
"github.com/jfrog/jfrog-cli-core/v2/common/build"
coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/artifactory"
"github.com/jfrog/jfrog-client-go/http/httpclient"
rtutils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"net/http"
"os"
"regexp"
"strings"
Expand All @@ -26,19 +30,24 @@ const (
FlagTypeCommitterReviewer FlagType = "gh-commiter"
FlagTypeOther FlagType = "other"
)
const releaseBundleInternalApi = "api/v2/release_bundle/internal/graph/"
const releaseBundleApi = "api/v2/release_bundle/records/"

const ghDefaultPredicateType = "https://jfrog.com/evidence/gh-commiter/v1"
const ghDefaultPredicateType = "https://jfrog.com/evidence/git-committer-reviewer/v1"

const gitFormat = `format:'{"commit":"%H","abbreviated_commit":"%h","tree":"%T","abbreviated_tree":"%t","parent":"%P","abbreviated_parent":"%p","subject":"%s","sanitized_subject_line":"%f","author":{"name":"%aN","email":"%aE","date":"%aD"},"commiter":{"name":"%cN","email":"%cE","date":"%cD"}}'`

type createGitHubEvidence struct {
createEvidenceBase
project string
buildName string
buildNumber string
project string
buildName string
buildNumber string
releaseBundle string
releaseBundleVersion string
}

func NewCreateGithub(serverDetails *coreConfig.ServerDetails, predicateFilePath, predicateType, markdownFilePath, key, keyId, project, buildName, buildNumber, typeFlag string) Command {
func NewCreateGithub(serverDetails *coreConfig.ServerDetails,
predicateFilePath, predicateType, markdownFilePath, key, keyId, project, buildName, buildNumber, typeFlag, rbName, rbVersion string) Command {
flagType := getFlagType(typeFlag)
return &createGitHubEvidence{
createEvidenceBase: createEvidenceBase{
Expand All @@ -50,20 +59,23 @@ func NewCreateGithub(serverDetails *coreConfig.ServerDetails, predicateFilePath,
keyId: keyId,
flagType: flagType,
},
project: project,
buildName: buildName,
buildNumber: buildNumber,
project: project,
buildName: buildName,
buildNumber: buildNumber,
releaseBundle: rbName,
releaseBundleVersion: rbVersion,
}
}

func getFlagType(typeFlag string) FlagType {
var flagType FlagType
if typeFlag == "gh-commiter" {
flagType = FlagTypeCommitterReviewer
} else {
flagType = FlagTypeOther
flagTypes := map[string]FlagType{
"gh-commiter": FlagTypeCommitterReviewer,
}

if flag, exists := flagTypes[typeFlag]; exists {
return flag
}
return flagType
return FlagTypeOther
}

func (c *createGitHubEvidence) CommandName() string {
Expand All @@ -78,6 +90,17 @@ func (c *createGitHubEvidence) Run() error {
if !isRunningUnderGitHubAction() {
return errors.New("this command is intended to be run under GitHub Actions")
}
if c.buildName == "" && c.releaseBundle == "" {
return errors.New("build name or release bundle name is required")
}

if c.releaseBundle != "" {
err := c.getBuildFromReleaseBundle()
if err != nil {
return err
}
}

evidencePredicate, err := c.committerReviewerEvidence()
if err != nil {
return err
Expand All @@ -88,9 +111,18 @@ func (c *createGitHubEvidence) Run() error {
log.Error("failed to create Artifactory client", err)
return err
}
subject, sha256, err := c.buildBuildInfoSubjectPath(artifactoryClient)
if err != nil {
return err

var subject, sha256 string
if c.releaseBundle != "" && c.releaseBundleVersion != "" {
subject, sha256, err = c.buildReleaseBundleSubjectPath(artifactoryClient)
if err != nil {
return err
}
} else {
subject, sha256, err = c.buildBuildInfoSubjectPath(artifactoryClient)
if err != nil {
return err
}
}
envelope, err := c.createEnvelopeWithPredicateAndPredicateType(subject,
sha256, ghDefaultPredicateType, evidencePredicate)
Expand Down Expand Up @@ -125,6 +157,18 @@ func (c *createGitHubEvidence) buildBuildInfoSubjectPath(artifactoryClient artif
return buildInfoPath, buildInfoChecksum, nil
}

func (c *createGitHubEvidence) buildReleaseBundleSubjectPath(artifactoryClient artifactory.ArtifactoryServicesManager) (string, string, error) {
repoKey := buildRepoKey(c.project)
manifestPath := buildManifestPath(repoKey, c.releaseBundle, c.releaseBundleVersion)

manifestChecksum, err := c.getFileChecksum(manifestPath, artifactoryClient)
if err != nil {
return "", "", err
}

return manifestPath, manifestChecksum, nil
}

func isRunningUnderGitHubAction() bool {
return os.Getenv("GITHUB_ACTIONS") == "true"
}
Expand Down Expand Up @@ -177,7 +221,7 @@ func marshalEvidenceToGitLogEntryView(evidence []byte) (*model.GitLogEntryView,

func (c *createGitHubEvidence) committerReviewerEvidence() ([]byte, error) {
if c.createEvidenceBase.flagType != FlagTypeCommitterReviewer {
return nil, errors.New("flag type must be gh-commiter")
return nil, errors.New("flag type is not supported")
}

createBuildConfiguration := c.createBuildConfiguration()
Expand All @@ -195,8 +239,108 @@ func (c *createGitHubEvidence) createBuildConfiguration() *build.BuildConfigurat
return buildConfiguration
}

func (c *createGitHubEvidence) getBuildFromReleaseBundle() error {
releaseBundleResponse, err := c.getPreviousReleaseBundle()
if err != nil {
return err
}
if len(releaseBundleResponse.ReleaseBundles) == 0 {
return errors.New("no release bundles found")
}
if len(releaseBundleResponse.ReleaseBundles) > 1 {
// Get the previous release bundle
c.releaseBundleVersion = releaseBundleResponse.ReleaseBundles[1].ReleaseBundleVersion
} else {
c.releaseBundleVersion = releaseBundleResponse.ReleaseBundles[0].ReleaseBundleVersion
}

rbv2Graph, err := c.getReleaseBundleGraph()
if err != nil {
return err
}
for _, node := range rbv2Graph.Root.Nodes {
if node.Type == "buildinfo" {
c.buildName = node.Name
c.buildNumber = node.Version
break
}
}
// Get the current release bundle to put the evidence on
c.releaseBundleVersion = releaseBundleResponse.ReleaseBundles[0].ReleaseBundleVersion
return nil
}

func (c *createGitHubEvidence) getReleaseBundleGraph() (*model.GraphResponse, error) {
authConfig, err := c.serverDetails.CreateArtAuthConfig()
if err != nil {
return nil, err
}

artifactoryApiUrl, err := rtutils.BuildUrl(c.serverDetails.GetLifecycleUrl(), releaseBundleInternalApi+c.releaseBundle+"/"+c.releaseBundleVersion, make(map[string]string))
if err != nil {
return nil, err
}

artHttpDetails := authConfig.CreateHttpClientDetails()
client, err := httpclient.ClientBuilder().Build()
if err != nil {
return nil, err
}
resp, body, _, err := client.SendGet(artifactoryApiUrl, true, artHttpDetails, "")
if err != nil {
return nil, err
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}
graphResponse := &model.GraphResponse{}
if err = json.Unmarshal(body, &graphResponse); err != nil {
return nil, errorutils.CheckError(err)
}
return graphResponse, nil
}

func (c *createGitHubEvidence) getPreviousReleaseBundle() (*model.ReleaseBundlesResponse, error) {
authConfig, err := c.serverDetails.CreateArtAuthConfig()
if err != nil {
return nil, err
}

queryParams := map[string]string{
"project": c.project,
}
artifactoryApiUrl, err := rtutils.BuildUrl(c.serverDetails.GetLifecycleUrl(), releaseBundleApi+c.releaseBundle, queryParams)
if err != nil {
return nil, err
}

artHttpDetails := authConfig.CreateHttpClientDetails()
client, err := httpclient.ClientBuilder().Build()
if err != nil {
return nil, err
}
resp, body, _, err := client.SendGet(artifactoryApiUrl, true, artHttpDetails, "")
if err != nil {
return nil, err
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return nil, err
}

response := &model.ReleaseBundlesResponse{}
if err := json.Unmarshal(body, response); err != nil {
return nil, errorutils.CheckError(err)
}
return response, nil
}

type PackageVersionResponseContent struct {
Version string `json:"Version,omitempty"`
}

func getGitCommitInfo(serverDetails *coreConfig.ServerDetails, createBuildConfiguration *build.BuildConfiguration, gitDetails artifactoryUtils.GitLogDetails) ([]byte, error) {
owner, repository, err := gitHubRepositoryDetails()
log.Info(fmt.Sprintf("owner: %s repository: %s", owner, repository))
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion evidence/create_github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (m *MockBuildAndVcsDetails) GetLastBuildLink(serverDetails *coreConfig.Serv

func TestNewCreateGithub(t *testing.T) {
serverDetails := &coreConfig.ServerDetails{}
command := NewCreateGithub(serverDetails, "path/to/predicate.json", "predicateType", "path/to/markdown.md", "key", "keyId", "myProject", "myBuild", "123", "gh-commiter")
command := NewCreateGithub(serverDetails, "path/to/predicate.json", "predicateType", "path/to/markdown.md", "key", "keyId", "myProject", "myBuild", "123", "gh-commiter", "", "")

assert.NotNil(t, command)

Expand Down
49 changes: 49 additions & 0 deletions evidence/model/release_bundle_graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package model

import "time"

type GraphResponse struct {
Root Root `json:"root,omitempty"`
}

// Root corresponds to the "root" key.
// All fields are pointers to allow for null values.
type Root struct {
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
Repository string `json:"repository,omitempty"`
CreatedMillis int64 `json:"created_millis,omitempty"`
Nodes []Node `json:"nodes,omitempty"`
Evidence []any `json:"evidence,omitempty"`
}

// Node represents items in the "nodes" array (recursively).
// Again, all fields are pointers to allow them to be nil.
type Node struct {
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
Repository string `json:"repository,omitempty"`
CreatedMillis int64 `json:"created_millis,omitempty"`
Nodes []Node `json:"nodes,omitempty"`
Evidence []any `json:"evidence,omitempty"`
PackageID string `json:"package_id,omitempty"`
}

type ReleaseBundle struct {
Status string `json:"status"`
RepositoryKey string `json:"repository_key"`
ReleaseBundleName string `json:"release_bundle_name"`
ReleaseBundleVersion string `json:"release_bundle_version"`
ServiceID string `json:"service_id"`
CreatedBy string `json:"created_by"`
Created time.Time `json:"created"`
}

type ReleaseBundlesResponse struct {
ReleaseBundles []ReleaseBundle `json:"release_bundles"`
Total int `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
Loading

0 comments on commit f8a53ad

Please sign in to comment.