Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions evidence/cli/command_application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cli

import (
"github.com/jfrog/jfrog-cli-artifactory/evidence/create"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
)

type evidenceApplicationCommand struct {
ctx *components.Context
execute execCommandFunc
}

func NewEvidenceApplicationCommand(ctx *components.Context, execute execCommandFunc) EvidenceCommands {
return &evidenceApplicationCommand{
ctx: ctx,
execute: execute,
}
}

func (eac *evidenceApplicationCommand) CreateEvidence(ctx *components.Context, serverDetails *config.ServerDetails) error {
if eac.ctx.GetStringFlagValue(sigstoreBundle) != "" {
return errorutils.CheckErrorf("--%s is not supported for application evidence.", sigstoreBundle)
}

err := eac.validateEvidenceApplicationContext(ctx)
if err != nil {
return err
}

createCmd := create.NewCreateEvidenceApplication(
serverDetails,
eac.ctx.GetStringFlagValue(predicate),
eac.ctx.GetStringFlagValue(predicateType),
eac.ctx.GetStringFlagValue(markdown),
eac.ctx.GetStringFlagValue(key),
eac.ctx.GetStringFlagValue(keyAlias),
eac.ctx.GetStringFlagValue(project),
eac.ctx.GetStringFlagValue(applicationKey),
eac.ctx.GetStringFlagValue(applicationVersion))
return eac.execute(createCmd)
}

func (eac *evidenceApplicationCommand) GetEvidence(ctx *components.Context, serverDetails *config.ServerDetails) error {
return nil
}

func (eac *evidenceApplicationCommand) VerifyEvidence(ctx *components.Context, serverDetails *config.ServerDetails) error {
return nil
}

func (eac *evidenceApplicationCommand) validateEvidenceApplicationContext(ctx *components.Context) error {
if !ctx.IsFlagSet(applicationKey) || assertValueProvided(ctx, applicationVersion) != nil {
return errorutils.CheckErrorf("--%s is a mandatory field for creating a Application evidence", releaseBundleVersion)
}
return nil
}
1 change: 1 addition & 0 deletions evidence/cli/command_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func createEvidence(ctx *components.Context) error {
evidenceCommands := map[string]func(*components.Context, execCommandFunc) EvidenceCommands{
subjectRepoPath: NewEvidenceCustomCommand,
releaseBundle: NewEvidenceReleaseBundleCommand,
applicationKey: NewEvidenceApplicationCommand,
buildName: NewEvidenceBuildCommand,
packageName: NewEvidencePackageCommand,
}
Expand Down
6 changes: 6 additions & 0 deletions evidence/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const (
packageVersion = "package-version"
packageRepoName = "package-repo-name"
typeFlag = "type"
applicationKey = "application-key"
applicationVersion = "application-version"

// Unique evidence flags
predicate = "predicate"
Expand Down Expand Up @@ -67,6 +69,8 @@ var flagsMap = map[string]components.Flag{
packageVersion: components.NewStringFlag(packageVersion, "Package version.", func(f *components.StringFlag) { f.Mandatory = false }),
packageRepoName: components.NewStringFlag(packageRepoName, "Package repository Name.", func(f *components.StringFlag) { f.Mandatory = false }),
typeFlag: components.NewStringFlag(typeFlag, "Type can contain 'gh-commiter' value.", func(f *components.StringFlag) { f.Mandatory = false }),
applicationKey: components.NewStringFlag(applicationKey, "Application key.", func(f *components.StringFlag) { f.Mandatory = false }),
applicationVersion: components.NewStringFlag(applicationVersion, "Application version.", func(f *components.StringFlag) { f.Mandatory = false }),

predicate: components.NewStringFlag(predicate, "Path to the predicate, arbitrary JSON. Mandatory unless --"+sigstoreBundle+" is used", func(f *components.StringFlag) { f.Mandatory = false }),
predicateType: components.NewStringFlag(predicateType, "Type of the predicate. Mandatory unless --"+sigstoreBundle+" is used", func(f *components.StringFlag) { f.Mandatory = false }),
Expand All @@ -93,6 +97,8 @@ var commandFlags = map[string][]string{
project,
releaseBundle,
releaseBundleVersion,
applicationKey,
applicationVersion,
buildName,
buildNumber,
packageName,
Expand Down
1 change: 1 addition & 0 deletions evidence/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func exec(command commands.Command) error {
var subjectTypes = []string{
subjectRepoPath,
releaseBundle,
applicationKey,
buildName,
packageName,
typeFlag,
Expand Down
81 changes: 81 additions & 0 deletions evidence/create/create_application_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package create

import (
"fmt"

"github.com/jfrog/jfrog-cli-artifactory/evidence"
"github.com/jfrog/jfrog-cli-artifactory/evidence/utils"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/artifactory"
"github.com/jfrog/jfrog-client-go/utils/log"
)

type createEvidenceApplication struct {
createEvidenceBase
project string
application string
applicationVersion string
}

func NewCreateEvidenceApplication(serverDetails *config.ServerDetails, predicateFilePath, predicateType, markdownFilePath, key, keyId, project, applicationName,
applicationVersion string) evidence.Command {
return &createEvidenceApplication{
createEvidenceBase: createEvidenceBase{
serverDetails: serverDetails,
predicateFilePath: predicateFilePath,
predicateType: predicateType,
markdownFilePath: markdownFilePath,
key: key,
keyId: keyId,
},
project: project,
application: applicationName,
applicationVersion: applicationVersion,
}
}

func (c *createEvidenceApplication) CommandName() string {
return "create-application-evidence"
}

func (c *createEvidenceApplication) ServerDetails() (*config.ServerDetails, error) {
return c.serverDetails, nil
}

func (c *createEvidenceApplication) Run() error {
artifactoryClient, err := c.createArtifactoryClient()
if err != nil {
log.Error("failed to create Artifactory client", err)
return err
}
subject, sha256, err := c.buildApplicationSubjectPath(artifactoryClient)
if err != nil {
return err
}
envelope, err := c.createEnvelope(subject, sha256)
if err != nil {
return err
}
err = c.uploadEvidence(envelope, subject)
if err != nil {
return err
}

return nil
}

func (c *createEvidenceApplication) buildApplicationSubjectPath(artifactoryClient artifactory.ArtifactoryServicesManager) (string, string, error) {
repoKey := utils.BuildReleaseBundleRepoKey(c.project) // currently same as release bundle repo key
manifestPath := buildManifestPathApplication(repoKey, c.application, c.applicationVersion)

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

return manifestPath, manifestChecksum, nil
}

func buildManifestPathApplication(repoKey, name, version string) string {
return fmt.Sprintf("%s/%s/%s/release-bundle.json.evd", repoKey, name, version)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manifest file has an .evd suffix?

Copy link

@avishayh avishayh Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this knowledge ".evd" already exist in JFROG CLI ? is there an API of RBV2 that returns this info?

}
150 changes: 150 additions & 0 deletions evidence/create/create_application_version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package create

import (
"testing"

"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/stretchr/testify/assert"
)

func TestNewCreateEvidenceApplication(t *testing.T) {
serverDetails := &config.ServerDetails{Url: "http://test.com", User: "testuser"}
predicateFilePath := "/path/to/predicate.json"
predicateType := "custom-predicate"
markdownFilePath := "/path/to/markdown.md"
key := "test-key"
keyId := "test-key-id"
project := "test-project"
applicationName := "test-app"
applicationVersion := "1.0.0"

cmd := NewCreateEvidenceApplication(serverDetails, predicateFilePath, predicateType, markdownFilePath, key, keyId, project, applicationName, applicationVersion)
createCmd, ok := cmd.(*createEvidenceApplication)
assert.True(t, ok)

assert.Equal(t, serverDetails, createCmd.serverDetails)
assert.Equal(t, predicateFilePath, createCmd.predicateFilePath)
assert.Equal(t, predicateType, createCmd.predicateType)
assert.Equal(t, markdownFilePath, createCmd.markdownFilePath)
assert.Equal(t, key, createCmd.key)
assert.Equal(t, keyId, createCmd.keyId)

assert.Equal(t, project, createCmd.project)
assert.Equal(t, applicationName, createCmd.application)
assert.Equal(t, applicationVersion, createCmd.applicationVersion)
}

func TestCreateEvidenceApplication_CommandName(t *testing.T) {
cmd := &createEvidenceApplication{}
assert.Equal(t, "create-application-evidence", cmd.CommandName())
}

func TestCreateEvidenceApplication_ServerDetails(t *testing.T) {
serverDetails := &config.ServerDetails{Url: "http://test.com", User: "testuser"}
cmd := &createEvidenceApplication{
createEvidenceBase: createEvidenceBase{serverDetails: serverDetails},
}

result, err := cmd.ServerDetails()
assert.NoError(t, err)
assert.Equal(t, serverDetails, result)
}

func TestBuildManifestPathApplication(t *testing.T) {
tests := []struct {
name string
repoKey string
appName string
version string
expected string
}{
{
name: "Valid_Basic_Path",
repoKey: "test-repo",
appName: "my-app",
version: "1.0.0",
expected: "test-repo/my-app/1.0.0/release-bundle.json.evd",
},
{
name: "With_Special_Characters",
repoKey: "test-repo-dev",
appName: "my-app-v2",
version: "1.0.0-beta",
expected: "test-repo-dev/my-app-v2/1.0.0-beta/release-bundle.json.evd",
},
{
name: "With_Numbers",
repoKey: "repo123",
appName: "app123",
version: "2.1.0",
expected: "repo123/app123/2.1.0/release-bundle.json.evd",
},
{
name: "Empty_Values",
repoKey: "",
appName: "",
version: "",
expected: "///release-bundle.json.evd",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := buildManifestPathApplication(tt.repoKey, tt.appName, tt.version)
assert.Equal(t, tt.expected, result)
})
}
}

func TestCreateEvidenceApplication_BuildApplicationSubjectPath(t *testing.T) {
tests := []struct {
name string
project string
application string
version string
expectPath string
}{
{
name: "Valid_Application_Path",
project: "test-project",
application: "my-app",
version: "1.0.0",
expectPath: "test-project-release-bundles-v2/my-app/1.0.0/release-bundle.json.evd",
},
{
name: "Different_Project",
project: "another-project",
application: "another-app",
version: "2.1.0",
expectPath: "another-project-release-bundles-v2/another-app/2.1.0/release-bundle.json.evd",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := createTestApplicationCommand()
cmd.project = tt.project
cmd.application = tt.application
cmd.applicationVersion = tt.version

repoKey := tt.project + "-release-bundles-v2"
path := buildManifestPathApplication(repoKey, cmd.application, cmd.applicationVersion)
assert.Equal(t, tt.expectPath, path)
})
}
}

func createTestApplicationCommand() *createEvidenceApplication {
return &createEvidenceApplication{
createEvidenceBase: createEvidenceBase{
serverDetails: &config.ServerDetails{Url: "http://test.com"},
predicateFilePath: "/test/predicate.json",
predicateType: "test-type",
key: "test-key",
keyId: "test-key-id",
},
project: "test-project",
application: "test-app",
applicationVersion: "1.0.0",
}
}
Loading