diff --git a/.github/workflows/pythonTests.yml b/.github/workflows/pythonTests.yml index ac635e67c..da1f3a616 100644 --- a/.github/workflows/pythonTests.yml +++ b/.github/workflows/pythonTests.yml @@ -59,7 +59,7 @@ jobs: - name: Setup Pipenv if: ${{ matrix.suite == 'pipenv' && matrix.os.name != 'macos' }} - run: python -m pip install pipenv + run: python -m pip install pipenv==2026.2.2 - name: Setup Twine if: ${{ matrix.suite == 'pip' && matrix.os.name != 'macos' }} diff --git a/buildtools/cli.go b/buildtools/cli.go index 1e8b708a0..4153628c3 100644 --- a/buildtools/cli.go +++ b/buildtools/cli.go @@ -1070,7 +1070,7 @@ func buildCmd(c *cli.Context) error { } // Extract build configuration and arguments - _, rtDetails, _, _, _, cleanArgs, buildConfiguration, err := extractDockerOptionsFromArgs(c.Args()) + _, rtDetails, _, skipLogin, _, cleanArgs, buildConfiguration, err := extractDockerOptionsFromArgs(c.Args()) if err != nil { return err } @@ -1079,10 +1079,11 @@ func buildCmd(c *cli.Context) error { return err } - // Login to the docker registry - err = loginCmd(c) - if err != nil { - return err + if !skipLogin { + err = loginCmd(c) + if err != nil { + return err + } } dockerOptions := strategies.DockerBuildOptions{ diff --git a/docker_test.go b/docker_test.go index 1598b5241..e777dd121 100644 --- a/docker_test.go +++ b/docker_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + urfavecli "github.com/urfave/cli" "github.com/jfrog/jfrog-client-go/utils/log" tests2 "github.com/jfrog/jfrog-cli-artifactory/utils/tests" @@ -1278,6 +1279,48 @@ CMD ["echo", "Hello from buildx"]`, baseImage) inttestutils.ContainerTestCleanup(t, serverDetails, artHttpDetails, imageNameOnly, buildName, tests.OciLocalRepo) } +// TestDockerBuildxSkipLoginFails verifies that --skip-login actually skips login. +// After logging out, buildx --push with --skip-login should fail because no auth is available. +func TestDockerBuildxSkipLoginFails(t *testing.T) { + cleanup := initDockerBuildTest(t) + defer cleanup() + + // Prevent urfave/cli from calling os.Exit on command errors + origOsExiter := urfavecli.OsExiter + urfavecli.OsExiter = func(code int) {} + defer func() { urfavecli.OsExiter = origOsExiter }() + + registryHost := *tests.ContainerRegistry + if parsedURL, err := url.Parse(registryHost); err == nil && parsedURL.Host != "" { + registryHost = parsedURL.Host + } + imageName := path.Join(registryHost, tests.OciLocalRepo, "test-skip-login") + imageTag := imageName + ":v1" + + workspace, err := filepath.Abs(tests.Out) + assert.NoError(t, err) + assert.NoError(t, fileutils.CreateDirIfNotExist(workspace)) + + baseImage := path.Join(registryHost, tests.OciRemoteRepo, "busybox:latest") + dockerfileContent := fmt.Sprintf(`FROM %s +CMD ["echo", "skip-login test"]`, baseImage) + dockerfilePath := filepath.Join(workspace, "Dockerfile") + assert.NoError(t, os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0644)) //#nosec G703 -- test code + + // Logout from registry so push requires fresh login + logoutCmd := exec.Command("docker", "logout", registryHost) + assert.NoError(t, logoutCmd.Run()) + + // With --skip-login, jf should NOT re-login, so push should fail + err = runJfrogCliWithoutAssertion("docker", "buildx", "build", + "--platform", "linux/amd64", + "-t", imageTag, "-f", dockerfilePath, "--push", "--skip-login", workspace) + assert.Error(t, err, "Expected failure: --skip-login should prevent auto-login, causing push to fail without auth") + + // Re-login for subsequent tests + runJfrogCli(t, "docker", "login", registryHost) +} + // TestDockerBuildWithVirtualRepo tests docker build with virtual repository func TestDockerBuildWithVirtualRepo(t *testing.T) { cleanup := initDockerBuildTest(t) diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 900b4a242..feafcc4b4 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -1914,7 +1914,7 @@ var commandFlags = map[string][]string{ serverId, skipLogin, }, DockerBuild: { - BuildName, BuildNumber, serverId, + BuildName, BuildNumber, serverId, skipLogin, }, DockerPromote: { targetDockerImage, sourceTag, targetTag, dockerPromoteCopy, url, user, password, accessToken, sshPassphrase, sshKeyPath,