Skip to content
Merged
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
10 changes: 6 additions & 4 deletions artifactory/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package cli

import (
"os"
"strconv"
"strings"

ioutils "github.com/jfrog/gofrog/io"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/buildinfo"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/container"
Expand Down Expand Up @@ -67,9 +71,6 @@ import (
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"github.com/pkg/errors"
"os"
"strconv"
"strings"
)

const (
Expand Down Expand Up @@ -483,6 +484,7 @@ func containerPushCmd(c *components.Context, containerManagerType containerutils
imageTag := c.GetArgumentAt(0)
targetRepo := c.GetArgumentAt(1)
skipLogin := c.GetBoolFlagValue("skip-login")
validateSha := c.GetBoolFlagValue("validate-sha")

buildConfiguration, err := common.CreateBuildConfigurationWithModule(c)
if err != nil {
Expand All @@ -494,7 +496,7 @@ func containerPushCmd(c *components.Context, containerManagerType containerutils
return
}
printDeploymentView, detailedSummary := log.IsStdErrTerminal(), c.GetBoolFlagValue("detailed-summary")
dockerPushCommand.SetThreads(threads).SetDetailedSummary(detailedSummary || printDeploymentView).SetCmdParams([]string{"push", imageTag}).SetSkipLogin(skipLogin).SetBuildConfiguration(buildConfiguration).SetRepo(targetRepo).SetServerDetails(artDetails).SetImageTag(imageTag)
dockerPushCommand.SetThreads(threads).SetDetailedSummary(detailedSummary || printDeploymentView).SetCmdParams([]string{"push", imageTag}).SetSkipLogin(skipLogin).SetBuildConfiguration(buildConfiguration).SetRepo(targetRepo).SetServerDetails(artDetails).SetImageTag(imageTag).SetValidateSha(validateSha)
err = commandWrappers.ShowDockerDeprecationMessageIfNeeded(containerManagerType, dockerPushCommand.IsGetRepoSupported)
if err != nil {
return
Expand Down
10 changes: 10 additions & 0 deletions artifactory/commands/container/containermanagerbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type ContainerCommandBase struct {
repo string
buildConfiguration *build.BuildConfiguration
serverDetails *config.ServerDetails
validateSha bool
}

func (ccb *ContainerCommandBase) ImageTag() string {
Expand All @@ -30,6 +31,15 @@ func (ccb *ContainerCommandBase) SetImageTag(imageTag string) *ContainerCommandB
return ccb
}

func (ccb *ContainerCommandBase) SetValidateSha(validateSha bool) *ContainerCommandBase {
ccb.validateSha = validateSha
return ccb
}

func (ccb *ContainerCommandBase) IsValidateSha() bool {
return ccb.validateSha
}

// Returns the repository name that contains this image.
func (ccb *ContainerCommandBase) GetRepo() (string, error) {
// The repository name is saved after first calling this function.
Expand Down
77 changes: 70 additions & 7 deletions artifactory/commands/container/push.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package container

import (
"fmt"
"path"

commandsutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container"
containerutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container"
"github.com/jfrog/jfrog-cli-core/v2/common/build"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
servicesutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
clientutils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/content"
"github.com/jfrog/jfrog-client-go/utils/log"
)

type PushCommand struct {
Expand All @@ -21,7 +23,7 @@ type PushCommand struct {
result *commandsutils.Result
}

func NewPushCommand(containerManagerType container.ContainerManagerType) *PushCommand {
func NewPushCommand(containerManagerType containerutils.ContainerManagerType) *PushCommand {
return &PushCommand{
ContainerCommand: ContainerCommand{
containerManagerType: containerManagerType,
Expand All @@ -47,6 +49,15 @@ func (pc *PushCommand) IsDetailedSummary() bool {
return pc.detailedSummary
}

func (pc *PushCommand) SetValidateSha(validateSha bool) *PushCommand {
pc.ContainerCommandBase.SetValidateSha(validateSha)
return pc
}

func (pc *PushCommand) IsValidateSha() bool {
return pc.ContainerCommandBase.IsValidateSha()
}

func (pc *PushCommand) Result() *commandsutils.Result {
return pc.result
}
Expand All @@ -60,8 +71,8 @@ func (pc *PushCommand) Run() error {
if err := pc.init(); err != nil {
return err
}
if pc.containerManagerType == container.DockerClient {
err := container.ValidateClientApiVersion()
if pc.containerManagerType == containerutils.DockerClient {
err := containerutils.ValidateClientApiVersion()
if err != nil {
return err
}
Expand All @@ -75,11 +86,12 @@ func (pc *PushCommand) Run() error {
return err
}
// Perform push.
cm := container.NewManager(pc.containerManagerType)
cm := containerutils.NewManager(pc.containerManagerType)
err = cm.RunNativeCmd(pc.cmdParams)
if err != nil {
return err
}

toCollect, err := pc.buildConfiguration.IsCollectBuildInfo()
if err != nil {
return err
Expand All @@ -103,10 +115,57 @@ func (pc *PushCommand) Run() error {
if err != nil {
return err
}
builder, err := container.NewLocalAgentBuildInfoBuilder(pc.image, repo, buildName, buildNumber, pc.BuildConfiguration().GetProject(), serviceManager, container.Push, cm)

// If SHA validation is enabled, log it
if pc.IsValidateSha() {
log.Info("Performing SHA-based validation for Docker push...")
// Get image SHA from the container manager
imageSha256, err := cm.Id(pc.image)
if err != nil {
return err
}
log.Debug("Using image SHA256 for validation: " + imageSha256)

// Use RemoteAgentBuildInfoBuilder for SHA-based validation
remoteBuilder, err := containerutils.NewRemoteAgentBuildInfoBuilder(pc.image, repo, buildName, buildNumber, pc.BuildConfiguration().GetProject(), serviceManager, imageSha256)
if err != nil {
return err
}

if toCollect {
if err := build.SaveBuildGeneralDetails(buildName, buildNumber, pc.buildConfiguration.GetProject()); err != nil {
return err
}
buildInfoModule, err := remoteBuilder.Build(pc.BuildConfiguration().GetModule())
if err != nil {
return err
}
if buildInfoModule == nil {
return errorutils.CheckError(fmt.Errorf("failed to create build info module: module is nil"))
}
if err = build.SaveBuildInfo(buildName, buildNumber, pc.BuildConfiguration().GetProject(), buildInfoModule); err != nil {
return errorutils.CheckError(fmt.Errorf("failed to save build info: %w", err))
}
}

if pc.IsDetailedSummary() {
if !toCollect {
// No build info collection triggered yet, build it now for the summary
if _, err = remoteBuilder.Build(""); err != nil {
return errorutils.CheckError(fmt.Errorf("failed to build summary info: %w", err))
}
}
return pc.layersMapToFileTransferDetails(serverDetails.ArtifactoryUrl, remoteBuilder.GetLayers())
}
return nil
}

// Standard path: use LocalAgentBuildInfoBuilder for tag-based validation
builder, err := containerutils.NewLocalAgentBuildInfoBuilder(pc.image, repo, buildName, buildNumber, pc.BuildConfiguration().GetProject(), serviceManager, containerutils.Push, cm)
if err != nil {
return err
}

if toCollect {
if err := build.SaveBuildGeneralDetails(buildName, buildNumber, pc.buildConfiguration.GetProject()); err != nil {
return err
Expand All @@ -119,10 +178,11 @@ func (pc *PushCommand) Run() error {
return err
}
}

if pc.IsDetailedSummary() {
if !toCollect {
// The build-info collection hasn't been triggered at this point, and we do need it for handling the detailed summary.
// We are therefore skipping setting mage build name/number props before running build-info collection.
// We are therefore skipping setting image build name/number props before running build-info collection.
builder.SetSkipTaggingLayers(true)
_, err = builder.Build("")
if err != nil {
Expand All @@ -131,6 +191,7 @@ func (pc *PushCommand) Run() error {
}
return pc.layersMapToFileTransferDetails(serverDetails.ArtifactoryUrl, builder.GetLayers())
}

return nil
}

Expand Down Expand Up @@ -163,3 +224,5 @@ func (pc *PushCommand) CommandName() string {
func (pc *PushCommand) ServerDetails() (*config.ServerDetails, error) {
return pc.serverDetails, nil
}

// Backward compatibility: If --validate-sha is not set, the legacy tag-based validation path is used, ensuring no change for existing users.
7 changes: 5 additions & 2 deletions cliutils/flagkit/flags.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package flagkit

import (
"strconv"

"github.com/jfrog/jfrog-cli-artifactory/cliutils/cmddefs"
commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils"
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
"strconv"
)

const (
Expand Down Expand Up @@ -323,6 +324,7 @@ const (
// Build tool flags
deploymentThreads = "deployment-threads"
skipLogin = "skip-login"
validateSha = "validate-sha"

// Unique docker promote flags
dockerPromotePrefix = "docker-promote-"
Expand Down Expand Up @@ -654,7 +656,7 @@ var commandFlags = map[string][]string{
},
ContainerPush: {
BuildName, BuildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath,
serverId, skipLogin, threads, Project, detailedSummary,
serverId, skipLogin, threads, Project, detailedSummary, validateSha,
},
ContainerPull: {
BuildName, BuildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath,
Expand Down Expand Up @@ -972,6 +974,7 @@ var flagsMap = map[string]components.Flag{

// Docker specific commands flags
skipLogin: components.NewBoolFlag(skipLogin, "[Default: false] Set to true if you'd like the command to skip performing docker login.", components.WithBoolDefaultValueFalse()),
validateSha: components.NewBoolFlag(validateSha, "[Default: false] Set to true to enable SHA validation during Docker push.", components.WithBoolDefaultValueFalse()),
Copy link
Collaborator

Choose a reason for hiding this comment

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

flag documentation has to be updated in jfrog/jfrog-documentation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, will add this there as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

watches: components.NewStringFlag(watches, "[Optional] A comma-separated(,) list of Xray watches, to determine Xray's violations creation.", components.SetMandatoryFalse()),
repoPath: components.NewStringFlag(repoPath, "[Optional] Target repo path, to enable Xray to determine watches accordingly.", components.SetMandatoryFalse()),
licenses: components.NewBoolFlag(licenses, "[Default: false] Set to true if you'd like to receive licenses from Xray scanning.", components.WithBoolDefaultValueFalse()),
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ require (
sigs.k8s.io/yaml v1.4.0 // indirect
)

//replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250429081008-4c70b8d467b9
replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250527091824-60a3b4b741aa

//replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20250508130334-f159cff9b11a

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ github.com/jfrog/froggit-go v1.17.0 h1:20Ie787WO27SwB2MOHDvsR6yN7fA5WfRnuAbmUqz1
github.com/jfrog/froggit-go v1.17.0/go.mod h1:HvDkfFfJwIdsXFdqaB+utvD2cLDRmaC3kF8otYb6Chw=
github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s=
github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4=
github.com/jfrog/jfrog-cli-core/v2 v2.58.7 h1:njRlkJjNZ1cvG25S/6T4h+ouI+ZRABN6xZN87UIzB/M=
github.com/jfrog/jfrog-cli-core/v2 v2.58.7/go.mod h1:ZXcipUeTTEQ/phqHdbCh4wJ5Oo4QVDxzQBREQ0J9mDc=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250527091824-60a3b4b741aa h1:ybm7alLNcgeVRaM4a4Ma1kjzFN5jxe5yIMvdmDATEWM=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20250527091824-60a3b4b741aa/go.mod h1:ZXcipUeTTEQ/phqHdbCh4wJ5Oo4QVDxzQBREQ0J9mDc=
github.com/jfrog/jfrog-client-go v1.53.1 h1:GDRLUDs6hhfGNjqbI+bjc3ApgBHnpVwURM+f26PVfyw=
github.com/jfrog/jfrog-client-go v1.53.1/go.mod h1:XxYs2QtlTm92yqJ5O4j4vzWI8d4sDtKQUT1miNHMgnw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down