diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml
index 23a98a6e9..86487ff15 100644
--- a/.github/workflows/analysis.yml
+++ b/.github/workflows/analysis.yml
@@ -13,13 +13,14 @@ jobs:
- name: Checkout Source
uses: actions/checkout@v4
- - name: Install Go
- uses: actions/setup-go@v3
+ - name: Setup Go
+ uses: actions/setup-go@v5
with:
go-version: 1.20.x
+ cache: false
- name: Static Code Analysis
- uses: golangci/golangci-lint-action@v3
+ uses: golangci/golangci-lint-action@v4
with:
args: |
--timeout 5m --out-${NO_FUTURE}format colored-line-number --enable errcheck,gosimple,govet,ineffassign,staticcheck,typecheck,unused,gocritic,asasalint,asciicheck,errchkjson,exportloopref,forcetypeassert,makezero,nilerr,unparam,unconvert,wastedassign,usestdlibvars
@@ -31,10 +32,11 @@ jobs:
- name: Checkout Source
uses: actions/checkout@v4
- - name: Install Go
- uses: actions/setup-go@v3
+ - name: Setup Go
+ uses: actions/setup-go@v5
with:
go-version: 1.20.x
+ cache: false
- name: Run Gosec Security Scanner
# Temporarily set version 2.18.0 to workaround https://github.com/securego/gosec/issues/1046
diff --git a/.github/workflows/embedded-jar-test.yml b/.github/workflows/embedded-jar-test.yml
deleted file mode 100644
index a2ff93497..000000000
--- a/.github/workflows/embedded-jar-test.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-# This test verifies that gradle-dep-tree.jar and maven-dep-tree.jar are kept up-to-date with the version specified in buildscripts/download-jars.js.
-# It accomplishes this by downloading the JARs and executing a "git diff" command.
-# In case there are any differences detected, the test will result in failure.
-name: Embedded Jars Tests
-on:
- push:
- branches:
- - '**'
- tags-ignore:
- - '**'
- pull_request:
-jobs:
- test:
- runs-on: ubuntu-latest
- env:
- GOPROXY: direct
- steps:
- - uses: actions/checkout@v4
-
- - name: Download JARs
- run: buildscripts/download-jars.sh
-
- - name: Check Diff
- run: git diff --exit-code
-
- - name: Log if Failure
- run: echo "::warning::Please run ./buildscripts/download-jars to use compatible Maven and Gradle dependency tree JARs."
- if: ${{ failure() }}
-
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a3026f842..ea9a56db8 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -28,13 +28,14 @@ jobs:
- name: Install pipenv & poetry
run: python -m pip install pipenv poetry
- - name: Install Go
- uses: actions/setup-go@v3
+ - name: Setup Go
+ uses: actions/setup-go@v5
with:
go-version: 1.20.x
+ cache: false
- name: Install NuGet
- uses: nuget/setup-nuget@v1
+ uses: nuget/setup-nuget@v2
with:
nuget-version: 6.x
diff --git a/artifactory/commands/buildinfo/adddependencies.go b/artifactory/commands/buildinfo/adddependencies.go
index fc389aa2b..d35d3731e 100644
--- a/artifactory/commands/buildinfo/adddependencies.go
+++ b/artifactory/commands/buildinfo/adddependencies.go
@@ -2,6 +2,7 @@ package buildinfo
import (
"errors"
+ ioutils "github.com/jfrog/gofrog/io"
regxp "regexp"
"strconv"
@@ -316,15 +317,12 @@ func convertFileInfoToDependencies(files map[string]*fileutils.FileDetails) []bu
func searchItems(spec *spec.SpecFiles, servicesManager artifactory.ArtifactoryServicesManager) (resultReader *content.ContentReader, err error) {
temp := []*content.ContentReader{}
var searchParams services.SearchParams
- var reader *content.ContentReader
defer func() {
for _, reader := range temp {
- e := reader.Close()
- if err == nil {
- err = e
- }
+ ioutils.Close(reader, &err)
}
}()
+ var reader *content.ContentReader
for i := 0; i < len(spec.Files); i++ {
searchParams, err = utils.GetSearchParams(spec.Get(i))
if err != nil {
diff --git a/artifactory/commands/dotnet/dotnetcommand.go b/artifactory/commands/dotnet/dotnetcommand.go
index 60c79e4e0..54ddebe93 100644
--- a/artifactory/commands/dotnet/dotnetcommand.go
+++ b/artifactory/commands/dotnet/dotnetcommand.go
@@ -118,12 +118,7 @@ func (dc *DotnetCommand) Exec() (err error) {
return err
}
defer func() {
- if callbackFunc != nil {
- e := callbackFunc()
- if err == nil {
- err = e
- }
- }
+ err = errors.Join(err, callbackFunc())
}()
if err = buildInfoModule.CalcDependencies(); err != nil {
if dc.isDotnetTestCommand() {
@@ -272,10 +267,7 @@ func InitNewConfig(configDirPath, repoName string, server *config.ServerDetails,
}
log.Debug("Nuget config file created at:", configFile.Name())
defer func() {
- e := configFile.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(configFile.Close()))
}()
// We would prefer to write the NuGet configuration using the `nuget add source` command,
diff --git a/artifactory/commands/generic/delete.go b/artifactory/commands/generic/delete.go
index 38f93af67..367965dc2 100644
--- a/artifactory/commands/generic/delete.go
+++ b/artifactory/commands/generic/delete.go
@@ -1,6 +1,7 @@
package generic
import (
+ ioutils "github.com/jfrog/gofrog/io"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-client-go/artifactory/services"
@@ -36,12 +37,7 @@ func (dc *DeleteCommand) Run() (err error) {
if err != nil {
return
}
- defer func() {
- e := reader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(reader, &err)
allowDelete := true
if !dc.quiet {
allowDelete, err = utils.ConfirmDelete(reader)
@@ -72,10 +68,7 @@ func (dc *DeleteCommand) GetPathsToDelete() (contentReader *content.ContentReade
var temp []*content.ContentReader
defer func() {
for _, reader := range temp {
- e := reader.Close()
- if err == nil {
- err = e
- }
+ ioutils.Close(reader, &err)
}
}()
for i := 0; i < len(dc.Spec().Files); i++ {
@@ -95,12 +88,7 @@ func (dc *DeleteCommand) GetPathsToDelete() (contentReader *content.ContentReade
if err != nil {
return nil, err
}
- defer func() {
- e := tempMergedReader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(tempMergedReader, &err)
// After merge, remove top chain dirs as we may encounter duplicates and collisions between files and directories to delete.
// For example:
// Reader1: {"a"}
diff --git a/artifactory/commands/generic/download.go b/artifactory/commands/generic/download.go
index 293508572..ef5c7f5ab 100644
--- a/artifactory/commands/generic/download.go
+++ b/artifactory/commands/generic/download.go
@@ -119,23 +119,13 @@ func (dc *DownloadCommand) download() (err error) {
log.Error(err)
}
if summary != nil {
- defer func() {
- e := summary.ArtifactsDetailsReader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer gofrog.Close(summary.ArtifactsDetailsReader, &err)
// If 'detailed summary' was requested, then the reader should not be closed here.
// It will be closed after it will be used to generate the summary.
if dc.DetailedSummary() {
dc.result.SetReader(summary.TransferDetailsReader)
} else {
- defer func() {
- e := summary.TransferDetailsReader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer gofrog.Close(summary.TransferDetailsReader, &err)
}
totalDownloaded = summary.TotalSucceeded
totalFailed = summary.TotalFailed
@@ -168,10 +158,7 @@ func (dc *DownloadCommand) download() (err error) {
var tmpRoot string
tmpRoot, err = createDownloadResultEmptyTmpReflection(summary.TransferDetailsReader)
defer func() {
- e := fileutils.RemoveTempDir(tmpRoot)
- if err == nil {
- err = e
- }
+ err = errors.Join(err, fileutils.RemoveTempDir(tmpRoot))
}()
if err != nil {
return err
diff --git a/artifactory/commands/generic/search.go b/artifactory/commands/generic/search.go
index cad7f1890..b5cf9ff40 100644
--- a/artifactory/commands/generic/search.go
+++ b/artifactory/commands/generic/search.go
@@ -1,6 +1,7 @@
package generic
import (
+ "errors"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
clientartutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
@@ -26,7 +27,7 @@ func (sc *SearchCommand) Run() error {
return err
}
-func (sc *SearchCommand) Search() (contentReader *content.ContentReader, err error) {
+func (sc *SearchCommand) Search() (*content.ContentReader, error) {
// Service Manager
serverDetails, err := sc.ServerDetails()
if errorutils.CheckError(err) != nil {
@@ -38,28 +39,15 @@ func (sc *SearchCommand) Search() (contentReader *content.ContentReader, err err
}
// Search Loop
log.Info("Searching artifacts...")
- var searchResults []*content.ContentReader
+
+ searchResults, callbackFunc, err := utils.SearchFiles(servicesManager, sc.Spec())
defer func() {
- for _, reader := range searchResults {
- e := reader.Close()
- if err == nil {
- err = e
- }
- }
+ err = errors.Join(err, callbackFunc())
}()
- for i := 0; i < len(sc.Spec().Files); i++ {
- searchParams, err := utils.GetSearchParams(sc.Spec().Get(i))
- if err != nil {
- log.Error(err)
- return nil, err
- }
- reader, err := servicesManager.SearchFiles(searchParams)
- if err != nil {
- log.Error(err)
- return nil, err
- }
- searchResults = append(searchResults, reader)
+ if err != nil {
+ return nil, err
}
+
reader, err := utils.AqlResultToSearchResult(searchResults)
if err != nil {
return nil, err
diff --git a/artifactory/commands/generic/upload.go b/artifactory/commands/generic/upload.go
index b16e83ffd..e346a9d53 100644
--- a/artifactory/commands/generic/upload.go
+++ b/artifactory/commands/generic/upload.go
@@ -5,10 +5,7 @@ import (
buildInfo "github.com/jfrog/build-info-go/entities"
- "os"
- "strconv"
- "time"
-
+ ioutils "github.com/jfrog/gofrog/io"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/common/build"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
@@ -19,6 +16,8 @@ import (
ioUtils "github.com/jfrog/jfrog-client-go/utils/io"
"github.com/jfrog/jfrog-client-go/utils/io/content"
"github.com/jfrog/jfrog-client-go/utils/log"
+ "strconv"
+ "time"
)
type UploadCommand struct {
@@ -81,7 +80,7 @@ func (uc *UploadCommand) upload() (err error) {
}
// Create Service Manager:
- uc.uploadConfiguration.MinChecksumDeploySize, err = getMinChecksumDeploySize()
+ uc.uploadConfiguration.MinChecksumDeploySize, err = utils.GetMinChecksumDeploySize()
if err != nil {
return
}
@@ -140,12 +139,7 @@ func (uc *UploadCommand) upload() (err error) {
}
if summary != nil {
artifactsDetailsReader = summary.ArtifactsDetailsReader
- defer func() {
- e := artifactsDetailsReader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(artifactsDetailsReader, &err)
// If 'detailed summary' was requested, then the reader should not be closed here.
// It will be closed after it will be used to generate the summary.
if uc.DetailedSummary() {
@@ -197,19 +191,6 @@ func (uc *UploadCommand) upload() (err error) {
return
}
-func getMinChecksumDeploySize() (int64, error) {
- minChecksumDeploySize := os.Getenv("JFROG_CLI_MIN_CHECKSUM_DEPLOY_SIZE_KB")
- if minChecksumDeploySize == "" {
- return services.DefaultMinChecksumDeploy, nil
- }
- minSize, err := strconv.ParseInt(minChecksumDeploySize, 10, 64)
- err = errorutils.CheckError(err)
- if err != nil {
- return 0, err
- }
- return minSize * 1000, nil
-}
-
func getUploadParams(f *spec.File, configuration *utils.UploadConfiguration, buildProps string, addVcsProps bool) (uploadParams services.UploadParams, err error) {
uploadParams = services.NewUploadParams()
uploadParams.CommonParams, err = f.ToCommonParams()
@@ -277,12 +258,7 @@ func (uc *UploadCommand) handleSyncDeletes(syncDeletesProp string) (err error) {
if err != nil {
return err
}
- defer func() {
- e := resultItems.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(resultItems, &err)
_, err = servicesManager.DeleteFiles(resultItems)
return err
}
diff --git a/artifactory/commands/golang/publish.go b/artifactory/commands/golang/publish.go
index ba123c901..4b042c64c 100644
--- a/artifactory/commands/golang/publish.go
+++ b/artifactory/commands/golang/publish.go
@@ -4,6 +4,7 @@ import (
"archive/zip"
"bytes"
"encoding/json"
+ "errors"
"fmt"
buildinfo "github.com/jfrog/build-info-go/entities"
biutils "github.com/jfrog/build-info-go/utils"
@@ -57,10 +58,7 @@ func publishPackage(packageVersion, targetRepo, buildName, buildNumber, projectK
return nil, nil, err
}
defer func() {
- e := fileutils.RemoveTempDir(tempDirPath)
- if err == nil {
- err = e
- }
+ err = errors.Join(err, fileutils.RemoveTempDir(tempDirPath))
}()
var zipArtifact *buildinfo.Artifact
@@ -87,18 +85,17 @@ func publishPackage(packageVersion, targetRepo, buildName, buildNumber, projectK
version := version.NewVersion(artifactoryVersion)
if version.AtLeast(_go.ArtifactoryMinSupportedVersion) {
log.Debug("Creating info file", projectPath)
- pathToInfo, err := createInfoFile(packageVersion)
+ var pathToInfo string
+ pathToInfo, err = createInfoFile(packageVersion)
if err != nil {
return nil, nil, err
}
defer func() {
- e := os.Remove(pathToInfo)
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(os.Remove(pathToInfo)))
}()
if collectBuildInfo {
- infoArtifact, err := createInfoFileArtifact(pathToInfo, packageVersion)
+ var infoArtifact *buildinfo.Artifact
+ infoArtifact, err = createInfoFileArtifact(pathToInfo, packageVersion)
if err != nil {
return nil, nil, err
}
@@ -125,10 +122,7 @@ func createInfoFile(packageVersion string) (path string, err error) {
return "", errorutils.CheckError(err)
}
defer func() {
- e := file.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(file.Close()))
}()
_, err = file.Write(content)
if err != nil {
@@ -155,10 +149,7 @@ func readModFile(version, projectPath string, createArtifact bool) ([]byte, *bui
return nil, nil, err
}
defer func() {
- e := modFile.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(modFile.Close()))
}()
content, err := io.ReadAll(modFile)
if err != nil {
@@ -191,10 +182,7 @@ func archive(moduleName, version, projectPath, tempDir string, excludedPatterns
openedFile = true
defer func() {
if openedFile {
- e := tempFile.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(tempFile.Close()))
}
}()
if err = archiveProject(tempFile, projectPath, moduleName, version, excludedPatterns); err != nil {
@@ -216,10 +204,10 @@ func archive(moduleName, version, projectPath, tempDir string, excludedPatterns
}
}
// Sync the file before renaming it
- if err := tempFile.Sync(); err != nil {
+ if err = tempFile.Sync(); err != nil {
return "", nil, err
}
- if err := tempFile.Close(); err != nil {
+ if err = tempFile.Close(); err != nil {
return "", nil, err
}
openedFile = false
diff --git a/artifactory/commands/npm/publish.go b/artifactory/commands/npm/publish.go
index 5313d2d74..ea5e7e01f 100644
--- a/artifactory/commands/npm/publish.go
+++ b/artifactory/commands/npm/publish.go
@@ -3,7 +3,9 @@ package npm
import (
"archive/tar"
"compress/gzip"
+ "errors"
"fmt"
+ ioutils "github.com/jfrog/gofrog/io"
"io"
"os"
"path/filepath"
@@ -37,7 +39,7 @@ type NpmPublishCommandArgs struct {
executablePath string
workingDirectory string
collectBuildInfo bool
- packedFilePath string
+ packedFilePaths []string
packageInfo *biutils.PackageInfo
publishPath string
tarballProvided bool
@@ -172,11 +174,11 @@ func (npc *NpmPublishCommand) Run() (err error) {
return err
}
// We should delete the tarball we created
- return deleteCreatedTarballAndError(npc.packedFilePath, err)
+ return errors.Join(err, deleteCreatedTarball(npc.packedFilePaths))
}
if !npc.tarballProvided {
- if err := deleteCreatedTarball(npc.packedFilePath); err != nil {
+ if err := deleteCreatedTarball(npc.packedFilePaths); err != nil {
return err
}
}
@@ -197,12 +199,7 @@ func (npc *NpmPublishCommand) Run() (err error) {
if err != nil {
return err
}
- defer func() {
- e := npc.artifactsDetailsReader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(npc.artifactsDetailsReader, &err)
err = npmModule.AddArtifacts(buildArtifacts...)
if err != nil {
return errorutils.CheckError(err)
@@ -217,6 +214,7 @@ func (npc *NpmPublishCommand) CommandName() string {
}
func (npc *NpmPublishCommand) preparePrerequisites() error {
+ npc.packedFilePaths = make([]string, 0)
currentDir, err := os.Getwd()
if err != nil {
return errorutils.CheckError(err)
@@ -251,7 +249,7 @@ func (npc *NpmPublishCommand) preparePrerequisites() error {
func (npc *NpmPublishCommand) pack() error {
log.Debug("Creating npm package.")
- packageFileName, err := npm.Pack(npc.npmArgs, npc.executablePath)
+ packedFileNames, err := npm.Pack(npc.npmArgs, npc.executablePath)
if err != nil {
return err
}
@@ -261,8 +259,10 @@ func (npc *NpmPublishCommand) pack() error {
return err
}
- npc.packedFilePath = filepath.Join(tarballDir, packageFileName)
- log.Debug("Created npm package at", npc.packedFilePath)
+ for _, packageFileName := range packedFileNames {
+ npc.packedFilePaths = append(npc.packedFilePaths, filepath.Join(tarballDir, packageFileName))
+ }
+
return nil
}
@@ -279,34 +279,36 @@ func (npc *NpmPublishCommand) getTarballDir() (string, error) {
return dest, nil
}
-func (npc *NpmPublishCommand) publish() error {
- log.Debug("Deploying npm package.")
- if err := npc.readPackageInfoFromTarball(); err != nil {
- return err
- }
- target := fmt.Sprintf("%s/%s", npc.repo, npc.packageInfo.GetDeployPath())
-
- // If requested, perform a Xray binary scan before deployment. If a FailBuildError is returned, skip the deployment.
- if npc.xrayScan {
- fileSpec := spec.NewBuilder().
- Pattern(npc.packedFilePath).
- Target(npc.repo + "/").
- BuildSpec()
- err := commandsutils.ConditionalUploadScanFunc(npc.serverDetails, fileSpec, 1, npc.scanOutputFormat)
- if err != nil {
- return err
+func (npc *NpmPublishCommand) publish() (err error) {
+ for _, packedFilePath := range npc.packedFilePaths {
+ log.Debug("Deploying npm package.")
+ if err = npc.readPackageInfoFromTarball(packedFilePath); err != nil {
+ return
+ }
+ target := fmt.Sprintf("%s/%s", npc.repo, npc.packageInfo.GetDeployPath())
+
+ // If requested, perform a Xray binary scan before deployment. If a FailBuildError is returned, skip the deployment.
+ if npc.xrayScan {
+ fileSpec := spec.NewBuilder().
+ Pattern(packedFilePath).
+ Target(npc.repo + "/").
+ BuildSpec()
+ if err = commandsutils.ConditionalUploadScanFunc(npc.serverDetails, fileSpec, 1, npc.scanOutputFormat); err != nil {
+ return
+ }
}
+ err = errors.Join(err, npc.doDeploy(target, npc.serverDetails, packedFilePath))
}
- return npc.doDeploy(target, npc.serverDetails)
+ return
}
-func (npc *NpmPublishCommand) doDeploy(target string, artDetails *config.ServerDetails) error {
+func (npc *NpmPublishCommand) doDeploy(target string, artDetails *config.ServerDetails, packedFilePath string) error {
servicesManager, err := utils.CreateServiceManager(artDetails, -1, 0, false)
if err != nil {
return err
}
up := services.NewUploadParams()
- up.CommonParams = &specutils.CommonParams{Pattern: npc.packedFilePath, Target: target}
+ up.CommonParams = &specutils.CommonParams{Pattern: packedFilePath, Target: target}
var totalFailed int
if npc.collectBuildInfo || npc.detailedSummary {
if npc.collectBuildInfo {
@@ -341,12 +343,11 @@ func (npc *NpmPublishCommand) doDeploy(target string, artDetails *config.ServerD
}
}
if npc.detailedSummary {
- npc.result.SetReader(summary.TransferDetailsReader)
- npc.result.SetFailCount(totalFailed)
- npc.result.SetSuccessCount(summary.TotalSucceeded)
+ if err = npc.setDetailedSummary(summary); err != nil {
+ return err
+ }
} else {
- err = summary.TransferDetailsReader.Close()
- if err != nil {
+ if err = summary.TransferDetailsReader.Close(); err != nil {
return err
}
}
@@ -364,6 +365,29 @@ func (npc *NpmPublishCommand) doDeploy(target string, artDetails *config.ServerD
return nil
}
+func (npc *NpmPublishCommand) setDetailedSummary(summary *specutils.OperationSummary) (err error) {
+ npc.result.SetFailCount(npc.result.FailCount() + summary.TotalFailed)
+ npc.result.SetSuccessCount(npc.result.SuccessCount() + summary.TotalSucceeded)
+ if npc.result.Reader() == nil {
+ npc.result.SetReader(summary.TransferDetailsReader)
+ } else {
+ if err = npc.appendReader(summary); err != nil {
+ return
+ }
+ }
+ return
+}
+
+func (npc *NpmPublishCommand) appendReader(summary *specutils.OperationSummary) error {
+ readersSlice := []*content.ContentReader{npc.result.Reader(), summary.TransferDetailsReader}
+ reader, err := content.MergeReaders(readersSlice, content.DefaultKey)
+ if err != nil {
+ return err
+ }
+ npc.result.SetReader(reader)
+ return nil
+}
+
func (npc *NpmPublishCommand) setPublishPath() error {
log.Debug("Reading Package Json.")
@@ -394,21 +418,19 @@ func (npc *NpmPublishCommand) setPackageInfo() error {
}
log.Debug("The provided path is not a directory, we assume this is a compressed npm package")
npc.tarballProvided = true
- npc.packedFilePath = npc.publishPath
- return npc.readPackageInfoFromTarball()
+ // Sets the location of the provided tarball
+ npc.packedFilePaths = []string{npc.publishPath}
+ return npc.readPackageInfoFromTarball(npc.publishPath)
}
-func (npc *NpmPublishCommand) readPackageInfoFromTarball() (err error) {
- log.Debug("Extracting info from npm package:", npc.packedFilePath)
- tarball, err := os.Open(npc.packedFilePath)
+func (npc *NpmPublishCommand) readPackageInfoFromTarball(packedFilePath string) (err error) {
+ log.Debug("Extracting info from npm package:", packedFilePath)
+ tarball, err := os.Open(packedFilePath)
if err != nil {
return errorutils.CheckError(err)
}
defer func() {
- e := tarball.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(tarball.Close()))
}()
gZipReader, err := gzip.NewReader(tarball)
if err != nil {
@@ -420,7 +442,7 @@ func (npc *NpmPublishCommand) readPackageInfoFromTarball() (err error) {
hdr, err := tarReader.Next()
if err != nil {
if err == io.EOF {
- return errorutils.CheckErrorf("Could not find 'package.json' in the compressed npm package: " + npc.packedFilePath)
+ return errorutils.CheckErrorf("Could not find 'package.json' in the compressed npm package: " + packedFilePath)
}
return errorutils.CheckError(err)
}
@@ -429,25 +451,18 @@ func (npc *NpmPublishCommand) readPackageInfoFromTarball() (err error) {
if err != nil {
return errorutils.CheckError(err)
}
-
npc.packageInfo, err = biutils.ReadPackageInfo(packageJson, npc.npmVersion)
return err
}
}
}
-func deleteCreatedTarballAndError(packedFilePath string, currentError error) error {
- if err := deleteCreatedTarball(packedFilePath); err != nil {
- errorText := fmt.Sprintf("Two errors occurred: \n%s \n%s", currentError, err)
- return errorutils.CheckErrorf(errorText)
- }
- return currentError
-}
-
-func deleteCreatedTarball(packedFilePath string) error {
- if err := os.Remove(packedFilePath); err != nil {
- return errorutils.CheckError(err)
+func deleteCreatedTarball(packedFilesPath []string) error {
+ for _, packedFilePath := range packedFilesPath {
+ if err := os.Remove(packedFilePath); err != nil {
+ return errorutils.CheckError(err)
+ }
+ log.Debug("Successfully deleted the created npm package:", packedFilePath)
}
- log.Debug("Successfully deleted the created npm package:", packedFilePath)
return nil
}
diff --git a/artifactory/commands/npm/publish_test.go b/artifactory/commands/npm/publish_test.go
index 91174289e..d49d3cf36 100644
--- a/artifactory/commands/npm/publish_test.go
+++ b/artifactory/commands/npm/publish_test.go
@@ -9,10 +9,26 @@ import (
func TestReadPackageInfoFromTarball(t *testing.T) {
npmPublish := NewNpmPublishCommand()
- npmPublish.packedFilePath = filepath.Join("..", "testdata", "npm", "npm-example-0.0.3.tgz")
- err := npmPublish.readPackageInfoFromTarball()
- assert.NoError(t, err)
- assert.Equal(t, "npm-example", npmPublish.packageInfo.Name)
- assert.Equal(t, "0.0.3", npmPublish.packageInfo.Version)
+ var testCases = []struct {
+ filePath string
+ packageName string
+ packageVersion string
+ }{
+ {
+ filePath: filepath.Join("..", "testdata", "npm", "npm-example-0.0.3.tgz"),
+ packageName: "npm-example",
+ packageVersion: "0.0.3",
+ }, {
+ filePath: filepath.Join("..", "testdata", "npm", "npm-example-0.0.4.tgz"),
+ packageName: "npm-example",
+ packageVersion: "0.0.4",
+ },
+ }
+ for _, test := range testCases {
+ err := npmPublish.readPackageInfoFromTarball(test.filePath)
+ assert.NoError(t, err)
+ assert.Equal(t, test.packageName, npmPublish.packageInfo.Name)
+ assert.Equal(t, test.packageVersion, npmPublish.packageInfo.Version)
+ }
}
diff --git a/artifactory/commands/python/dependencies/cache.go b/artifactory/commands/python/dependencies/cache.go
index 3a15ad8df..71b5dd215 100644
--- a/artifactory/commands/python/dependencies/cache.go
+++ b/artifactory/commands/python/dependencies/cache.go
@@ -3,6 +3,7 @@ package dependencies
import (
"encoding/json"
buildinfo "github.com/jfrog/build-info-go/entities"
+ ioutils "github.com/jfrog/gofrog/io"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
"io"
@@ -31,12 +32,7 @@ func GetProjectDependenciesCache(cacheDir string) (cache *DependenciesCache, err
if errorutils.CheckError(err) != nil {
return nil, err
}
- defer func() {
- e := jsonFile.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(jsonFile, &err)
byteValue, err := io.ReadAll(jsonFile)
if errorutils.CheckError(err) != nil {
return nil, err
@@ -66,12 +62,7 @@ func UpdateDependenciesCache(updatedMap map[string]buildinfo.Dependency, cacheDi
if err != nil {
return errorutils.CheckError(err)
}
- defer func() {
- e := cacheFile.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(cacheFile, &err)
_, err = cacheFile.Write(content)
if err != nil {
return errorutils.CheckError(err)
diff --git a/artifactory/commands/python/dependencies/dependencies.go b/artifactory/commands/python/dependencies/dependencies.go
index 8ab970851..142855d7c 100644
--- a/artifactory/commands/python/dependencies/dependencies.go
+++ b/artifactory/commands/python/dependencies/dependencies.go
@@ -3,6 +3,7 @@ package dependencies
import (
"encoding/json"
"fmt"
+ ioutils "github.com/jfrog/gofrog/io"
"io"
"strings"
@@ -96,12 +97,7 @@ func getDependencyChecksumFromArtifactory(servicesManager artifactory.Artifactor
if err != nil {
return
}
- defer func() {
- e := stream.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(stream, &err)
result, err := io.ReadAll(stream)
if err != nil {
return
diff --git a/artifactory/commands/python/poetry.go b/artifactory/commands/python/poetry.go
index b9367b71c..da91baa99 100644
--- a/artifactory/commands/python/poetry.go
+++ b/artifactory/commands/python/poetry.go
@@ -47,10 +47,7 @@ func (pc *PoetryCommand) Run() (err error) {
}
defer func() {
if pythonBuildInfo != nil && err != nil {
- e := pythonBuildInfo.Clean()
- if e != nil {
- err = errors.New(err.Error() + "\n" + e.Error())
- }
+ err = errors.Join(err, pythonBuildInfo.Clean())
}
}()
err = pc.SetPypiRepoUrlWithCredentials()
diff --git a/artifactory/commands/python/python.go b/artifactory/commands/python/python.go
index defac7960..8d2de8b3e 100644
--- a/artifactory/commands/python/python.go
+++ b/artifactory/commands/python/python.go
@@ -43,10 +43,7 @@ func (pc *PythonCommand) Run() (err error) {
}
defer func() {
if pythonBuildInfo != nil && err != nil {
- e := pythonBuildInfo.Clean()
- if e != nil {
- err = errors.New(err.Error() + "\n" + e.Error())
- }
+ err = errors.Join(err, pythonBuildInfo.Clean())
}
}()
err = pc.SetPypiRepoUrlWithCredentials()
diff --git a/artifactory/commands/terraform/terraformpublish.go b/artifactory/commands/terraform/terraformpublish.go
index 6dbdefd36..0a6e854e7 100644
--- a/artifactory/commands/terraform/terraformpublish.go
+++ b/artifactory/commands/terraform/terraformpublish.go
@@ -1,7 +1,9 @@
package terraform
import (
+ "errors"
buildInfo "github.com/jfrog/build-info-go/entities"
+ ioutils "github.com/jfrog/gofrog/io"
"github.com/jfrog/gofrog/parallel"
commandsUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
@@ -304,12 +306,7 @@ func readArtifactsFromSummary(summary *servicesUtils.OperationSummary) (artifact
if artifactsDetailsReader == nil {
return []buildInfo.Artifact{}, nil
}
- defer func() {
- e := artifactsDetailsReader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(artifactsDetailsReader, &err)
return servicesUtils.ConvertArtifactsDetailsToBuildInfoArtifacts(artifactsDetailsReader)
}
@@ -339,10 +336,7 @@ func checkIfTerraformModule(path string) (isModule bool, err error) {
return false, errorutils.CheckError(err)
}
defer func() {
- e := d.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, d.Close())
}()
files, err := d.Readdir(-1)
diff --git a/artifactory/commands/testdata/npm/npm-example-0.0.4.tgz b/artifactory/commands/testdata/npm/npm-example-0.0.4.tgz
new file mode 100644
index 000000000..5d3f4db17
Binary files /dev/null and b/artifactory/commands/testdata/npm/npm-example-0.0.4.tgz differ
diff --git a/artifactory/commands/transferconfig/transferconfig.go b/artifactory/commands/transferconfig/transferconfig.go
index ddc6ce01c..42af06f40 100644
--- a/artifactory/commands/transferconfig/transferconfig.go
+++ b/artifactory/commands/transferconfig/transferconfig.go
@@ -3,6 +3,7 @@ package transferconfig
import (
"bytes"
"context"
+ "errors"
"fmt"
"net/http"
"os"
@@ -105,10 +106,7 @@ func (tcc *TransferConfigCommand) Run() (err error) {
tcc.LogTitle("Phase 2/5 - Export configuration from the source Artifactory")
exportPath, cleanUp, err := tcc.exportSourceArtifactory()
defer func() {
- cleanUpErr := cleanUp()
- if err == nil {
- err = cleanUpErr
- }
+ err = errors.Join(err, cleanUp())
}()
if err != nil {
return
@@ -313,9 +311,7 @@ func (tcc *TransferConfigCommand) getEncryptedItems(selectedSourceRepos map[util
return "", nil, err
}
defer func() {
- if reactivationErr := reactivateKeyEncryption(); err == nil {
- err = reactivationErr
- }
+ err = errors.Join(err, reactivateKeyEncryption())
}()
// Download artifactory.config.xml from the source Artifactory server.
diff --git a/artifactory/commands/transferconfig/utils.go b/artifactory/commands/transferconfig/utils.go
index 75d27310c..58c3b62c5 100644
--- a/artifactory/commands/transferconfig/utils.go
+++ b/artifactory/commands/transferconfig/utils.go
@@ -4,6 +4,7 @@ import (
"archive/zip"
"bytes"
"compress/flate"
+ "errors"
"io"
"os"
"path/filepath"
@@ -31,10 +32,7 @@ func archiveConfig(exportPath string, configXml string) (buffer *bytes.Buffer, r
return flate.NewWriter(out, flate.BestCompression)
})
defer func() {
- closeErr := writer.Close()
- if retErr == nil {
- retErr = errorutils.CheckError(closeErr)
- }
+ retErr = errors.Join(retErr, errorutils.CheckError(writer.Close()))
}()
err := handleTypoInAccessBootstrap(exportPath)
diff --git a/artifactory/commands/transferconfigmerge/transferconfigmerge.go b/artifactory/commands/transferconfigmerge/transferconfigmerge.go
index ea1c0d273..cf2b7a986 100644
--- a/artifactory/commands/transferconfigmerge/transferconfigmerge.go
+++ b/artifactory/commands/transferconfigmerge/transferconfigmerge.go
@@ -1,6 +1,7 @@
package transferconfigmerge
import (
+ "errors"
"fmt"
"reflect"
"strings"
@@ -358,9 +359,7 @@ func (tcmc *TransferConfigMergeCommand) decryptAndGetAllRemoteRepositories(remot
return
}
defer func() {
- if reactivationErr := reactivateKeyEncryption(); err == nil {
- err = reactivationErr
- }
+ err = errors.Join(err, reactivateKeyEncryption())
}()
var remoteRepositoryKeys []string
for _, remoteRepositoryDetails := range remoteRepositoriesDetails {
diff --git a/artifactory/commands/transferfiles/api/types.go b/artifactory/commands/transferfiles/api/types.go
index 7cbadd8f3..708ee49f0 100644
--- a/artifactory/commands/transferfiles/api/types.go
+++ b/artifactory/commands/transferfiles/api/types.go
@@ -47,6 +47,8 @@ type UploadChunk struct {
CheckExistenceInFilestore bool `json:"check_existence_in_filestore,omitempty"`
// True if should skip file filtering in the Data Transfer plugin
SkipFileFiltering bool `json:"skip_file_filtering,omitempty"`
+ // Minimum file size in bytes for which JFrog CLI performs checksum deploy optimization
+ MinCheckSumDeploySize int64 `json:"min_checksum_deploy_size,omitempty"`
}
type FileRepresentation struct {
diff --git a/artifactory/commands/transferfiles/delayedartifactshandler.go b/artifactory/commands/transferfiles/delayedartifactshandler.go
index a5a20cfc9..caf80fcde 100644
--- a/artifactory/commands/transferfiles/delayedartifactshandler.go
+++ b/artifactory/commands/transferfiles/delayedartifactshandler.go
@@ -2,6 +2,7 @@ package transferfiles
import (
"encoding/json"
+ "errors"
"fmt"
"os"
"path"
@@ -62,9 +63,7 @@ func getDelaysFilePrefix(repoKey string, phaseStartTime string) string {
func (mng *TransferDelayedArtifactsMng) start() (err error) {
defer func() {
if mng.delayedWriter != nil {
- if e := mng.delayedWriter.close(); err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(mng.delayedWriter.close()))
}
}()
diff --git a/artifactory/commands/transferfiles/errorshandler.go b/artifactory/commands/transferfiles/errorshandler.go
index db9a8cb3e..b075d03b9 100644
--- a/artifactory/commands/transferfiles/errorshandler.go
+++ b/artifactory/commands/transferfiles/errorshandler.go
@@ -2,6 +2,7 @@ package transferfiles
import (
"encoding/json"
+ "errors"
"fmt"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/api"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/state"
@@ -119,10 +120,7 @@ func (mng *TransferErrorsMng) start() (err error) {
return err
}
defer func() {
- e := mng.errorWriterMng.retryable.closeWriter()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, mng.errorWriterMng.retryable.closeWriter())
}()
writerMng.retryable = errorWriter{writer: writerRetry, filePath: retryFilePath}
// Init the content writer which is responsible for writing 'skipped errors' into files.
@@ -136,10 +134,7 @@ func (mng *TransferErrorsMng) start() (err error) {
return err
}
defer func() {
- e := mng.errorWriterMng.skipped.closeWriter()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, mng.errorWriterMng.skipped.closeWriter())
}()
writerMng.skipped = errorWriter{writer: writerSkip, filePath: skipFilePath}
mng.errorWriterMng = writerMng
diff --git a/artifactory/commands/transferfiles/fulltransfer.go b/artifactory/commands/transferfiles/fulltransfer.go
index 4c7a38118..a60d48e41 100644
--- a/artifactory/commands/transferfiles/fulltransfer.go
+++ b/artifactory/commands/transferfiles/fulltransfer.go
@@ -175,7 +175,8 @@ func (m *fullTransferPhase) searchAndHandleFolderContents(params folderParams, p
CheckExistenceInFilestore: m.checkExistenceInFilestore,
// Skip file filtering in the Data Transfer plugin if it is already enabled in the JFrog CLI.
// The local generated filter is enabled in the JFrog CLI for target Artifactory servers >= 7.55.
- SkipFileFiltering: m.locallyGeneratedFilter.IsEnabled(),
+ SkipFileFiltering: m.locallyGeneratedFilter.IsEnabled(),
+ MinCheckSumDeploySize: m.minCheckSumDeploySize,
}
var result []servicesUtils.ResultItem
diff --git a/artifactory/commands/transferfiles/longpropertycheck.go b/artifactory/commands/transferfiles/longpropertycheck.go
index 99c091a1a..5791c2fb8 100644
--- a/artifactory/commands/transferfiles/longpropertycheck.go
+++ b/artifactory/commands/transferfiles/longpropertycheck.go
@@ -2,6 +2,7 @@ package transferfiles
import (
"encoding/json"
+ "errors"
"fmt"
"io"
"sync"
@@ -228,10 +229,7 @@ func runAqlService(serviceManager artifactory.ArtifactoryServicesManager, query
}
defer func() {
if reader != nil {
- e := reader.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(reader.Close()))
}
}()
respBody, err := io.ReadAll(reader)
diff --git a/artifactory/commands/transferfiles/phase.go b/artifactory/commands/transferfiles/phase.go
index bb93adf87..99ced8985 100644
--- a/artifactory/commands/transferfiles/phase.go
+++ b/artifactory/commands/transferfiles/phase.go
@@ -38,6 +38,7 @@ type transferPhase interface {
setPackageType(packageType string)
setDisabledDistinctiveAql()
setStopSignal(stopSignal chan os.Signal)
+ setMinCheckSumDeploySize(minCheckSumDeploySize int64)
StopGracefully()
}
@@ -62,6 +63,7 @@ type phaseBase struct {
stopSignal chan os.Signal
// Optimization in Artifactory version 7.37 and above enables the exclusion of setting DISTINCT in SQL queries
disabledDistinctiveAql bool
+ minCheckSumDeploySize int64
}
func (pb *phaseBase) ShouldStop() bool {
@@ -147,6 +149,10 @@ func (pb *phaseBase) setDisabledDistinctiveAql() {
pb.disabledDistinctiveAql = true
}
+func (pb *phaseBase) setMinCheckSumDeploySize(minCheckSumDeploySize int64) {
+ pb.minCheckSumDeploySize = minCheckSumDeploySize
+}
+
func (pb *phaseBase) setStopSignal(stopSignal chan os.Signal) {
pb.stopSignal = stopSignal
}
diff --git a/artifactory/commands/transferfiles/transfer.go b/artifactory/commands/transferfiles/transfer.go
index 62b451026..92840e75f 100644
--- a/artifactory/commands/transferfiles/transfer.go
+++ b/artifactory/commands/transferfiles/transfer.go
@@ -390,15 +390,16 @@ func (tdc *TransferFilesCommand) transferSingleRepo(sourceRepoKey string, target
return
}
defer func() {
- e := restoreFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, restoreFunc())
}()
if err = tdc.initCurThreads(buildInfoRepo); err != nil {
return
}
+ minChecksumDeploySize, err := utils.GetMinChecksumDeploySize()
+ if err != nil {
+ return
+ }
for currentPhaseId := 0; currentPhaseId < NumberOfPhases; currentPhaseId++ {
if tdc.shouldStop() {
return
@@ -413,7 +414,7 @@ func (tdc *TransferFilesCommand) transferSingleRepo(sourceRepoKey string, target
if err = tdc.stateManager.SetRepoPhase(currentPhaseId); err != nil {
return
}
- if err = tdc.startPhase(newPhase, sourceRepoKey, buildInfoRepo, *repoSummary, srcUpService); err != nil {
+ if err = tdc.startPhase(newPhase, sourceRepoKey, buildInfoRepo, *repoSummary, srcUpService, minChecksumDeploySize); err != nil {
return
}
}
@@ -479,8 +480,8 @@ func (tdc *TransferFilesCommand) removeOldFilesIfNeeded(repos []string) error {
return nil
}
-func (tdc *TransferFilesCommand) startPhase(newPhase *transferPhase, repo string, buildInfoRepo bool, repoSummary serviceUtils.RepositorySummary, srcUpService *srcUserPluginService) error {
- tdc.initNewPhase(*newPhase, srcUpService, repoSummary, repo, buildInfoRepo)
+func (tdc *TransferFilesCommand) startPhase(newPhase *transferPhase, repo string, buildInfoRepo bool, repoSummary serviceUtils.RepositorySummary, srcUpService *srcUserPluginService, minChecksumDeploySize int64) error {
+ tdc.initNewPhase(*newPhase, srcUpService, repoSummary, repo, buildInfoRepo, minChecksumDeploySize)
skip, err := (*newPhase).shouldSkipPhase()
if err != nil || skip {
return err
@@ -546,7 +547,7 @@ func (tdc *TransferFilesCommand) handleStop(srcUpService *srcUserPluginService)
}, &newPhase
}
-func (tdc *TransferFilesCommand) initNewPhase(newPhase transferPhase, srcUpService *srcUserPluginService, repoSummary serviceUtils.RepositorySummary, repoKey string, buildInfoRepo bool) {
+func (tdc *TransferFilesCommand) initNewPhase(newPhase transferPhase, srcUpService *srcUserPluginService, repoSummary serviceUtils.RepositorySummary, repoKey string, buildInfoRepo bool, minChecksumDeploySize int64) {
newPhase.setContext(tdc.context)
newPhase.setRepoKey(repoKey)
newPhase.setCheckExistenceInFilestore(tdc.checkExistenceInFilestore)
@@ -561,6 +562,7 @@ func (tdc *TransferFilesCommand) initNewPhase(newPhase transferPhase, srcUpServi
newPhase.setPackageType(repoSummary.PackageType)
newPhase.setLocallyGeneratedFilter(tdc.locallyGeneratedFilter)
newPhase.setStopSignal(tdc.stopSignal)
+ newPhase.setMinCheckSumDeploySize(minChecksumDeploySize)
}
// Get all local and build-info repositories of the input server
@@ -638,10 +640,7 @@ func (tdc *TransferFilesCommand) cleanup(originalErr error, sourceRepos []string
err = originalErr
// Quit progress bar (before printing logs)
if tdc.progressbar != nil {
- e := tdc.progressbar.Quit()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, tdc.progressbar.Quit())
}
// Transferring finished successfully
if originalErr == nil {
diff --git a/artifactory/commands/transferfiles/utils.go b/artifactory/commands/transferfiles/utils.go
index a9b77f067..622f768e4 100644
--- a/artifactory/commands/transferfiles/utils.go
+++ b/artifactory/commands/transferfiles/utils.go
@@ -430,6 +430,7 @@ func uploadByChunks(files []api.FileRepresentation, uploadTokensChan chan Upload
TargetAuth: createTargetAuth(base.targetRtDetails, base.proxyKey),
CheckExistenceInFilestore: base.checkExistenceInFilestore,
SkipFileFiltering: base.locallyGeneratedFilter.IsEnabled(),
+ MinCheckSumDeploySize: base.minCheckSumDeploySize,
}
for _, item := range files {
diff --git a/artifactory/commands/utils/precheckrunner/checkrunner.go b/artifactory/commands/utils/precheckrunner/checkrunner.go
index eca8a5e2e..62b435453 100644
--- a/artifactory/commands/utils/precheckrunner/checkrunner.go
+++ b/artifactory/commands/utils/precheckrunner/checkrunner.go
@@ -2,6 +2,7 @@ package precheckrunner
import (
"context"
+ "errors"
"fmt"
"github.com/gookit/color"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
@@ -116,9 +117,7 @@ func (pcr *PreCheckRunner) Run(context context.Context, serverDetails *config.Se
}
// Execute checks
defer func() {
- if e := pcr.cleanup(); e != nil && err == nil {
- err = e
- }
+ err = errors.Join(err, pcr.cleanup())
}()
var checkPassed bool
for i, check := range pcr.checks {
diff --git a/artifactory/commands/utils/result_test.go b/artifactory/commands/utils/result_test.go
index 81423a2ff..380fdbe76 100644
--- a/artifactory/commands/utils/result_test.go
+++ b/artifactory/commands/utils/result_test.go
@@ -2,6 +2,7 @@ package utils
import (
biutils "github.com/jfrog/build-info-go/utils"
+ ioutils "github.com/jfrog/gofrog/io"
testsutils "github.com/jfrog/jfrog-client-go/utils/tests"
"os"
"path"
@@ -43,12 +44,7 @@ func createTempDeployableArtifactFile() (filePath string, err error) {
if errorutils.CheckError(err) != nil {
return
}
- defer func() {
- e := summary.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(summary, &err)
tmpDir, err := fileutils.CreateTempDir()
if err != nil {
return
diff --git a/artifactory/commands/utils/transfer.go b/artifactory/commands/utils/transfer.go
index 71fabd4b1..ae9117b25 100644
--- a/artifactory/commands/utils/transfer.go
+++ b/artifactory/commands/utils/transfer.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/gocarina/gocsv"
+ ioutils "github.com/jfrog/gofrog/io"
logutils "github.com/jfrog/jfrog-cli-core/v2/utils/log"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
@@ -54,12 +55,7 @@ func CreateCSVFile(filePrefix string, items interface{}, timeStarted time.Time)
return
}
csvPath = summaryCsv.Name()
- defer func() {
- e := summaryCsv.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(summaryCsv, &err)
// Marshal JSON typed items array to CSV file
err = errorutils.CheckError(gocsv.MarshalFile(items, summaryCsv))
return
diff --git a/artifactory/commands/utils/yarnutils.go b/artifactory/commands/utils/yarnutils.go
index cde89b031..6d7880152 100644
--- a/artifactory/commands/utils/yarnutils.go
+++ b/artifactory/commands/utils/yarnutils.go
@@ -2,6 +2,7 @@ package utils
import (
"encoding/json"
+ ioutils "github.com/jfrog/gofrog/io"
"io"
"strconv"
"strings"
@@ -54,12 +55,7 @@ func getDependencyInfo(name, ver string, previousBuildDependencies map[string]*e
if err != nil {
return
}
- defer func() {
- e := stream.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(stream, &err)
var result []byte
result, err = io.ReadAll(stream)
if err != nil {
diff --git a/artifactory/utils/container/buildinfo.go b/artifactory/utils/container/buildinfo.go
index 7a42f4ac4..412f2f4ab 100644
--- a/artifactory/utils/container/buildinfo.go
+++ b/artifactory/utils/container/buildinfo.go
@@ -2,6 +2,7 @@ package container
import (
"encoding/json"
+ ioutils "github.com/jfrog/gofrog/io"
"os"
"path"
"strings"
@@ -94,12 +95,7 @@ func setBuildProperties(buildName, buildNumber, project string, imageLayers []ut
return
}
reader := content.NewContentReader(pathToFile, content.DefaultKey)
- defer func() {
- e := reader.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(reader, &err)
_, err = serviceManager.SetProps(services.PropsParams{Reader: reader, Props: props})
return
}
@@ -118,9 +114,7 @@ func writeLayersToFile(layers []utils.ResultItem) (filePath string, err error) {
if err != nil {
return
}
- defer func() {
- err = writer.Close()
- }()
+ defer ioutils.Close(writer, &err)
for _, layer := range layers {
writer.Write(layer)
}
@@ -200,11 +194,7 @@ func performSearch(imagePathPattern string, serviceManager artifactory.Artifacto
if err != nil {
return nil, err
}
- defer func() {
- if deferErr := reader.Close(); err == nil {
- err = deferErr
- }
- }()
+ defer ioutils.Close(reader, &err)
resultMap = make(map[string]*utils.ResultItem)
for resultItem := new(utils.ResultItem); reader.NextRecord(resultItem) == nil; resultItem = new(utils.ResultItem) {
resultMap[resultItem.Name] = resultItem
@@ -224,11 +214,7 @@ func performMultiPlatformImageSearch(imagePathPattern string, serviceManager art
if err != nil {
return nil, err
}
- defer func() {
- if deferErr := reader.Close(); err == nil {
- err = deferErr
- }
- }()
+ defer ioutils.Close(reader, &err)
pathToSha2 := make(map[string]string)
pathToImageLayers := make(map[string][]*utils.ResultItem)
resultMap = make(map[string][]*utils.ResultItem)
diff --git a/artifactory/utils/npm/config-list.go b/artifactory/utils/npm/config-list.go
index 477648dbf..d129ef53c 100644
--- a/artifactory/utils/npm/config-list.go
+++ b/artifactory/utils/npm/config-list.go
@@ -1,6 +1,7 @@
package npm
import (
+ "errors"
gofrogcmd "github.com/jfrog/gofrog/io"
npmutils "github.com/jfrog/jfrog-cli-core/v2/utils/npm"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
@@ -12,10 +13,7 @@ import (
func GetConfigList(npmFlags []string, executablePath string) (data []byte, err error) {
pipeReader, pipeWriter := io.Pipe()
defer func(pipeReader *io.PipeReader) {
- e := pipeReader.Close()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, pipeReader.Close())
}(pipeReader)
npmFlags = append(npmFlags, "--json=false")
diff --git a/artifactory/utils/npm/pack.go b/artifactory/utils/npm/pack.go
index 0cefe8cd8..9f9686709 100644
--- a/artifactory/utils/npm/pack.go
+++ b/artifactory/utils/npm/pack.go
@@ -8,13 +8,13 @@ import (
"github.com/jfrog/jfrog-client-go/utils/errorutils"
)
-func Pack(npmFlags []string, executablePath string) (string, error) {
+func Pack(npmFlags []string, executablePath string) ([]string, error) {
configListCmdConfig := createPackCmdConfig(executablePath, npmFlags)
output, err := gofrogcmd.RunCmdOutput(configListCmdConfig)
if err != nil {
- return "", errorutils.CheckError(err)
+ return []string{}, errorutils.CheckError(err)
}
- return getPackageFileNameFromOutput(output)
+ return getPackageFileNameFromOutput(output), nil
}
func createPackCmdConfig(executablePath string, splitFlags []string) *npmutils.NpmConfig {
@@ -27,8 +27,7 @@ func createPackCmdConfig(executablePath string, splitFlags []string) *npmutils.N
}
}
-func getPackageFileNameFromOutput(output string) (string, error) {
+func getPackageFileNameFromOutput(output string) []string {
output = strings.TrimSpace(output)
- lines := strings.Split(output, "\n")
- return strings.TrimSpace(lines[len(lines)-1]), nil
+ return strings.Split(output, "\n")
}
diff --git a/artifactory/utils/npm/pack_test.go b/artifactory/utils/npm/pack_test.go
index 8b8533b4b..68ec6c5c8 100644
--- a/artifactory/utils/npm/pack_test.go
+++ b/artifactory/utils/npm/pack_test.go
@@ -1,36 +1,66 @@
package npm
import (
+ biutils "github.com/jfrog/build-info-go/build/utils"
+ "github.com/jfrog/build-info-go/utils"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
+ "github.com/jfrog/jfrog-client-go/utils/log"
+ testsUtils "github.com/jfrog/jfrog-client-go/utils/tests"
"github.com/stretchr/testify/assert"
"os"
"path/filepath"
"testing"
)
-const testdataDir = "../testdata/npm/"
+const minimumWorkspacesNpmVersion = "7.24.2"
-func TestGetPackageFileNameFromOutput(t *testing.T) {
- tests := []struct {
- testName string
- outputTestDataFile string
- expectedPackageFilename string
- }{
- {"Get package filename for npm 6", "npmPackOutputV6", "npm-example-0.0.3.tgz"},
- {"Get package filename for npm 7", "npmPackOutputV7", "npm-example-ver0.0.3.tgz"},
- }
- for _, test := range tests {
- t.Run(test.testName, func(t *testing.T) {
- output, err := os.ReadFile(filepath.Join(testdataDir, test.outputTestDataFile))
- if err != nil {
- assert.NoError(t, err)
- return
- }
- actualFilename, err := getPackageFileNameFromOutput(string(output))
- if err != nil {
- assert.NoError(t, err)
- return
- }
- assert.Equal(t, test.expectedPackageFilename, actualFilename)
- })
+func TestNpmPackWorkspaces(t *testing.T) {
+
+ npmVersion, executablePath, err := biutils.GetNpmVersionAndExecPath(nil)
+ assert.NoError(t, err)
+ // In npm under v7 skip test
+ if npmVersion.Compare(minimumWorkspacesNpmVersion) > 0 {
+ log.Info("Test skipped as this function in not supported in npm version " + npmVersion.GetVersion())
+ return
}
+
+ tmpDir, createTempDirCallback := tests.CreateTempDirWithCallbackAndAssert(t)
+ defer createTempDirCallback()
+
+ npmProjectPath := filepath.Join("..", "..", "..", "tests", "testdata", "npm-workspaces")
+ err = utils.CopyDir(npmProjectPath, tmpDir, true, nil)
+ assert.NoError(t, err)
+
+ cwd, err := os.Getwd()
+ assert.NoError(t, err)
+ chdirCallback := testsUtils.ChangeDirWithCallback(t, cwd, tmpDir)
+ defer chdirCallback()
+
+ packedFileNames, err := Pack([]string{"--workspaces", "--verbose"}, executablePath)
+ assert.NoError(t, err)
+
+ expected := []string{"module1-1.0.0.tgz", "module2-1.0.0.tgz"}
+ assert.Equal(t, expected, packedFileNames)
+}
+
+func TestNpmPack(t *testing.T) {
+
+ _, executablePath, err := biutils.GetNpmVersionAndExecPath(nil)
+ assert.NoError(t, err)
+ tmpDir, createTempDirCallback := tests.CreateTempDirWithCallbackAndAssert(t)
+ defer createTempDirCallback()
+ npmProjectPath := filepath.Join("..", "..", "..", "tests", "testdata", "npm-workspaces")
+ err = utils.CopyDir(npmProjectPath, tmpDir, false, nil)
+ assert.NoError(t, err)
+
+ cwd, err := os.Getwd()
+ assert.NoError(t, err)
+ chdirCallback := testsUtils.ChangeDirWithCallback(t, cwd, tmpDir)
+ defer chdirCallback()
+
+ packedFileNames, err := Pack([]string{"--verbose"}, executablePath)
+ assert.NoError(t, err)
+
+ expected := []string{"npm-pack-test-1.0.0.tgz"}
+ assert.Equal(t, expected, packedFileNames)
}
diff --git a/artifactory/utils/search.go b/artifactory/utils/search.go
index a8888c82e..c72d68d76 100644
--- a/artifactory/utils/search.go
+++ b/artifactory/utils/search.go
@@ -2,6 +2,9 @@ package utils
import (
"encoding/json"
+ "errors"
+ ioutils "github.com/jfrog/gofrog/io"
+ "github.com/jfrog/jfrog-client-go/artifactory"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-client-go/artifactory/services"
@@ -68,12 +71,7 @@ func AqlResultToSearchResult(readers []*content.ContentReader) (contentReader *c
if err != nil {
return nil, err
}
- defer func() {
- e := writer.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(writer, &err)
for _, reader := range readers {
for searchResult := new(utils.ResultItem); reader.NextRecord(searchResult) == nil; searchResult = new(utils.ResultItem) {
if err != nil {
@@ -147,12 +145,7 @@ func SearchResultNoDate(reader *content.ContentReader) (contentReader *content.C
if err != nil {
return nil, err
}
- defer func() {
- e := writer.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(writer, &err)
for resultItem := new(SearchResult); reader.NextRecord(resultItem) == nil; resultItem = new(SearchResult) {
if err != nil {
return nil, err
@@ -170,3 +163,28 @@ func SearchResultNoDate(reader *content.ContentReader) (contentReader *content.C
contentReader = content.NewContentReader(writer.GetFilePath(), writer.GetArrayKey())
return
}
+
+func SearchFiles(servicesManager artifactory.ArtifactoryServicesManager, spec *spec.SpecFiles) (searchResults []*content.ContentReader, callbackFunc func() error, err error) {
+ callbackFunc = func() error {
+ var errs error
+ for _, reader := range searchResults {
+ errs = errors.Join(errs, reader.Close())
+ }
+ return errs
+ }
+
+ var curSearchParams services.SearchParams
+ var curReader *content.ContentReader
+ for i := 0; i < len(spec.Files); i++ {
+ curSearchParams, err = GetSearchParams(spec.Get(i))
+ if err != nil {
+ return
+ }
+ curReader, err = servicesManager.SearchFiles(curSearchParams)
+ if err != nil {
+ return
+ }
+ searchResults = append(searchResults, curReader)
+ }
+ return
+}
diff --git a/artifactory/utils/testdata/npm/npmPackOutputV6 b/artifactory/utils/testdata/npm/npmPackOutputV6
deleted file mode 100644
index b0eb2668b..000000000
--- a/artifactory/utils/testdata/npm/npmPackOutputV6
+++ /dev/null
@@ -1,28 +0,0 @@
-> npm-example@0.0.3 prepack /Users/robin/proj/project-examples/npm-example
-> echo pre-helloworld
-
-pre-helloworld
-
-> npm-example@0.0.3 postpack /Users/robin/proj/project-examples/npm-example
-> echo post-helloworld
-
-post-helloworld
-npm notice
-npm notice 📦 npm-example@0.0.3
-npm notice === Tarball Contents ===
-npm notice 181B helloworld.js
-npm notice 276B package.json
-npm notice 2.8kB README.md
-npm notice 5.5kB npm-example-ver0.0.3.tgz
-npm notice 97B .jfrog/projects/npm.yaml
-npm notice === Tarball Details ===
-npm notice name: npm-example
-npm notice version: 0.0.3
-npm notice filename: npm-example-0.0.3.tgz
-npm notice package size: 7.5 kB
-npm notice unpacked size: 8.8 kB
-npm notice shasum: fd0a95ccbb62ff833cd89cf4bb5296486c9a63aa
-npm notice integrity: sha512-pMRH9mUXGZzeC[...]eJk8tQc1qSbRA==
-npm notice total files: 5
-npm notice
-npm-example-0.0.3.tgz
\ No newline at end of file
diff --git a/artifactory/utils/testdata/npm/npmPackOutputV7 b/artifactory/utils/testdata/npm/npmPackOutputV7
deleted file mode 100644
index 602a97930..000000000
--- a/artifactory/utils/testdata/npm/npmPackOutputV7
+++ /dev/null
@@ -1,28 +0,0 @@
-> npm-example@ver0.0.3 prepack
-> echo pre-helloworld
-
-pre-helloworld
-
-> npm-example@ver0.0.3 postpack
-> echo post-helloworld
-
-post-helloworld
-npm notice
-npm notice 📦 npm-example@ver0.0.3
-npm notice === Tarball Contents ===
-npm notice 2.8kB README.md
-npm notice 97B .jfrog/projects/npm.yaml
-npm notice 181B helloworld.js
-npm notice 3.5kB npm-example-ver0.0.3.tgz
-npm notice 279B package.json
-npm notice === Tarball Details ===
-npm notice name: npm-example
-npm notice version: ver0.0.3
-npm notice filename: npm-example-ver0.0.3.tgz
-npm notice package size: 5.5 kB
-npm notice unpacked size: 6.8 kB
-npm notice shasum: e3af25617b6c58c7f803d919949fc3d8993ce9dc
-npm notice integrity: sha512-nTrTk6ph83jLL[...]ipNTJhWUci8Wg==
-npm notice total files: 5
-npm notice
-npm-example-ver0.0.3.tgz
\ No newline at end of file
diff --git a/artifactory/utils/transfersettings.go b/artifactory/utils/transfersettings.go
index 1a3d36fd8..705743c31 100644
--- a/artifactory/utils/transfersettings.go
+++ b/artifactory/utils/transfersettings.go
@@ -3,6 +3,7 @@ package utils
import (
"bytes"
"encoding/json"
+ "errors"
"os"
"path/filepath"
@@ -54,10 +55,7 @@ func LoadTransferSettings() (settings *TransferSettings, err error) {
unlockFunc, err := lock.CreateLock(filepath.Join(locksDirPath, transferSettingsLockFile))
// Defer the lockFile.Unlock() function before throwing a possible error to avoid deadlock situations.
defer func() {
- e := unlockFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, unlockFunc())
}()
if err != nil {
return
@@ -99,10 +97,7 @@ func SaveTransferSettings(settings *TransferSettings) (err error) {
unlockFunc, err := lock.CreateLock(filepath.Join(locksDirPath, transferSettingsLockFile))
// Defer the lockFile.Unlock() function before throwing a possible error to avoid deadlock situations.
defer func() {
- e := unlockFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, unlockFunc())
}()
if err != nil {
return
diff --git a/artifactory/utils/upload.go b/artifactory/utils/upload.go
index 11829a5ab..ed00f67f9 100644
--- a/artifactory/utils/upload.go
+++ b/artifactory/utils/upload.go
@@ -1,8 +1,13 @@
package utils
import (
+ "os"
+ "strconv"
+
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/artifactory"
+ "github.com/jfrog/jfrog-client-go/artifactory/services"
+ "github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io"
)
@@ -18,3 +23,16 @@ type UploadConfiguration struct {
SplitCount int
MinSplitSizeMB int64
}
+
+func GetMinChecksumDeploySize() (int64, error) {
+ minChecksumDeploySize := os.Getenv("JFROG_CLI_MIN_CHECKSUM_DEPLOY_SIZE_KB")
+ if minChecksumDeploySize == "" {
+ return services.DefaultMinChecksumDeploy, nil
+ }
+ minSize, err := strconv.ParseInt(minChecksumDeploySize, 10, 64)
+ err = errorutils.CheckError(err)
+ if err != nil {
+ return 0, err
+ }
+ return minSize * 1000, nil
+}
diff --git a/artifactory/utils/utils.go b/artifactory/utils/utils.go
index ab4c6a882..1699a4d43 100644
--- a/artifactory/utils/utils.go
+++ b/artifactory/utils/utils.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
+ ioutils "github.com/jfrog/gofrog/io"
"io"
"net/http"
"net/url"
@@ -227,11 +228,7 @@ func RemoteUnmarshal(serviceManager artifactory.ArtifactoryServicesManager, remo
if err != nil {
return
}
- defer func() {
- if localErr := ioReaderCloser.Close(); err == nil {
- err = localErr
- }
- }()
+ defer ioutils.Close(ioReaderCloser, &err)
content, err := io.ReadAll(ioReaderCloser)
if err != nil {
return errorutils.CheckError(err)
diff --git a/buildscripts/download-jars.sh b/buildscripts/download-jars.sh
deleted file mode 100755
index 27f1d20b1..000000000
--- a/buildscripts/download-jars.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-# Please use this script to download the JAR files for maven-dep-tree and gradle-dep-tree into the directory utils/java/.
-# These JARs allow us to build Maven and Gradle dependency trees efficiently and without compilation.
-# Learn more about them here:
-# https://github.com/jfrog/gradle-dep-tree
-# https://github.com/jfrog/maven-dep-tree
-
-# Once you have updated the versions mentioned below, please execute this script from the root directory of the jfrog-cli-core to ensure the JAR files are updated.
-GRADLE_DEP_TREE_VERSION="3.0.1"
-# Changing this version also requires a change in mavenDepTreeVersion within utils/java/mvn.go.
-MAVEN_DEP_TREE_VERSION="1.1.0"
-
-curl -fL https://releases.jfrog.io/artifactory/oss-release-local/com/jfrog/gradle-dep-tree/${GRADLE_DEP_TREE_VERSION}/gradle-dep-tree-${GRADLE_DEP_TREE_VERSION}.jar -o utils/java/resources/gradle-dep-tree.jar
-curl -fL https://releases.jfrog.io/artifactory/oss-release-local/com/jfrog/maven-dep-tree/${MAVEN_DEP_TREE_VERSION}/maven-dep-tree-${MAVEN_DEP_TREE_VERSION}.jar -o utils/java/resources/maven-dep-tree.jar
diff --git a/common/build/buildutils.go b/common/build/buildutils.go
index df222bd5c..4184403aa 100644
--- a/common/build/buildutils.go
+++ b/common/build/buildutils.go
@@ -120,10 +120,7 @@ func saveBuildData(action interface{}, buildName, buildNumber, projectKey string
return err
}
defer func() {
- e := tempFile.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(tempFile.Close()))
}()
_, err = tempFile.Write(content.Bytes())
return err
@@ -149,10 +146,7 @@ func SaveBuildInfo(buildName, buildNumber, projectKey string, buildInfo *buildIn
return err
}
defer func() {
- e := tempFile.Close()
- if err == nil {
- err = errorutils.CheckError(e)
- }
+ err = errors.Join(err, errorutils.CheckError(tempFile.Close()))
}()
_, err = tempFile.Write(content.Bytes())
return errorutils.CheckError(err)
diff --git a/common/commands/config.go b/common/commands/config.go
index a0802387c..53ce635e4 100644
--- a/common/commands/config.go
+++ b/common/commands/config.go
@@ -118,10 +118,7 @@ func (cc *ConfigCommand) Run() (err error) {
unlockFunc, err := lock.CreateLock(lockDirPath)
// Defer the lockFile.Unlock() function before throwing a possible error to avoid deadlock situations.
defer func() {
- e := unlockFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, unlockFunc())
}()
if err != nil {
return
diff --git a/common/progressbar/filesprogressbar.go b/common/progressbar/filesprogressbar.go
index 17de11da0..159f11af9 100644
--- a/common/progressbar/filesprogressbar.go
+++ b/common/progressbar/filesprogressbar.go
@@ -1,6 +1,7 @@
package progressbar
import (
+ "errors"
"net/url"
"os"
"strings"
@@ -323,10 +324,7 @@ func ExecWithProgress(cmd CommandWithProgress) (err error) {
if progressBar != nil {
cmd.SetProgress(progressBar)
defer func() {
- e := progressBar.Quit()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, progressBar.Quit())
}()
}
err = commands.Exec(cmd)
diff --git a/go.mod b/go.mod
index 0cc3899cd..a4c898986 100644
--- a/go.mod
+++ b/go.mod
@@ -7,25 +7,25 @@ require github.com/c-bata/go-prompt v0.2.5 // Should not be updated to 0.2.6 due
require (
github.com/buger/jsonparser v1.1.1
github.com/chzyer/readline v1.5.1
- github.com/forPelevin/gomoji v1.1.8
+ github.com/forPelevin/gomoji v1.2.0
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4
- github.com/jedib0t/go-pretty/v6 v6.5.4
- github.com/jfrog/build-info-go v1.9.23
+ github.com/jedib0t/go-pretty/v6 v6.5.6
+ github.com/jfrog/build-info-go v1.9.25
github.com/jfrog/gofrog v1.6.3
- github.com/jfrog/jfrog-client-go v1.37.1
+ github.com/jfrog/jfrog-client-go v1.39.0
github.com/magiconair/properties v1.8.7
github.com/manifoldco/promptui v0.9.0
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/spf13/viper v1.18.2
- github.com/stretchr/testify v1.8.4
+ github.com/stretchr/testify v1.9.0
github.com/urfave/cli v1.22.14
github.com/vbauerster/mpb/v7 v7.5.3
- golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
- golang.org/x/mod v0.15.0
+ golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
+ golang.org/x/mod v0.16.0
golang.org/x/sync v0.6.0
- golang.org/x/term v0.17.0
+ golang.org/x/term v0.18.0
golang.org/x/text v0.14.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -88,16 +88,16 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
- golang.org/x/crypto v0.19.0 // indirect
- golang.org/x/net v0.21.0 // indirect
- golang.org/x/sys v0.17.0 // indirect
- golang.org/x/tools v0.18.0 // indirect
+ golang.org/x/crypto v0.21.0 // indirect
+ golang.org/x/net v0.22.0 // indirect
+ golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/tools v0.19.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
-replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240228121257-3414cc0ffcb6
+// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240327154209-77a304635e42
-replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20240225113943-096bf22ca54c
+// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20240319160313-0093dee91fc1
// replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.3.3-0.20231223133729-ef57bd08cedc
diff --git a/go.sum b/go.sum
index 172a51532..a5a38cc2e 100644
--- a/go.sum
+++ b/go.sum
@@ -49,8 +49,8 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
-github.com/forPelevin/gomoji v1.1.8 h1:JElzDdt0TyiUlecy6PfITDL6eGvIaxqYH1V52zrd0qQ=
-github.com/forPelevin/gomoji v1.1.8/go.mod h1:8+Z3KNGkdslmeGZBC3tCrwMrcPy5GRzAD+gL9NAwMXg=
+github.com/forPelevin/gomoji v1.2.0 h1:9k4WVSSkE1ARO/BWywxgEUBvR/jMnao6EZzrql5nxJ8=
+github.com/forPelevin/gomoji v1.2.0/go.mod h1:8+Z3KNGkdslmeGZBC3tCrwMrcPy5GRzAD+gL9NAwMXg=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
@@ -79,16 +79,16 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jedib0t/go-pretty/v6 v6.5.4 h1:gOGo0613MoqUcf0xCj+h/V3sHDaZasfv152G6/5l91s=
-github.com/jedib0t/go-pretty/v6 v6.5.4/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg=
+github.com/jedib0t/go-pretty/v6 v6.5.6 h1:nKXVLqPfAwY7sWcYXdNZZZ2fjqDpAtj9UeWupgfUxSg=
+github.com/jedib0t/go-pretty/v6 v6.5.6/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg=
github.com/jfrog/archiver/v3 v3.6.0 h1:OVZ50vudkIQmKMgA8mmFF9S0gA47lcag22N13iV3F1w=
github.com/jfrog/archiver/v3 v3.6.0/go.mod h1:fCAof46C3rAXgZurS8kNRNdSVMKBbZs+bNNhPYxLldI=
-github.com/jfrog/build-info-go v1.8.9-0.20240225113943-096bf22ca54c h1:M1QiuCYGCYN1IiGyxogrLzfetYGkkhE2pgDh5K4Wo9A=
-github.com/jfrog/build-info-go v1.8.9-0.20240225113943-096bf22ca54c/go.mod h1:QHcKuesY4MrBVBuEwwBz4uIsX6mwYuMEDV09ng4AvAU=
+github.com/jfrog/build-info-go v1.9.25 h1:IkjydGQA/HjOWjRaoKq1hOEgCCyBEJwQgXJSo4WVBSA=
+github.com/jfrog/build-info-go v1.9.25/go.mod h1:doFB4bFDVHeGulD6GF9LzsrRaIOrSoklV9DgIAEqHgc=
github.com/jfrog/gofrog v1.6.3 h1:F7He0+75HcgCe6SGTSHLFCBDxiE2Ja0tekvvcktW6wc=
github.com/jfrog/gofrog v1.6.3/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg=
-github.com/jfrog/jfrog-client-go v1.28.1-0.20240228121257-3414cc0ffcb6 h1:W+79g2W3ARRhIZtBfG0t73fi4IlyiIRWwdm1tajOkkc=
-github.com/jfrog/jfrog-client-go v1.28.1-0.20240228121257-3414cc0ffcb6/go.mod h1:WhVrqiqhSNFwj58/RQIrJEd28PHH1LTD4eWE0vBXv1o=
+github.com/jfrog/jfrog-client-go v1.39.0 h1:GZ1qbpUDzYz8ZEycYicDkbVMN2H0VSCuz8mUNTyf7tc=
+github.com/jfrog/jfrog-client-go v1.39.0/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@@ -180,8 +180,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
@@ -211,14 +212,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
-golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
-golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
+golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
-golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -226,8 +227,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
-golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -260,15 +261,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
-golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
-golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
+golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -282,8 +283,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
-golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
+golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
+golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/lifecycle/common.go b/lifecycle/common.go
index e185cb578..5e1b2d0a2 100644
--- a/lifecycle/common.go
+++ b/lifecycle/common.go
@@ -2,10 +2,12 @@ package lifecycle
import (
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
+ "github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/lifecycle"
"github.com/jfrog/jfrog-client-go/lifecycle/services"
clientUtils "github.com/jfrog/jfrog-client-go/utils"
+ "github.com/jfrog/jfrog-client-go/utils/distribution"
)
const minimalLifecycleArtifactoryVersion = "7.63.2"
@@ -14,7 +16,6 @@ type releaseBundleCmd struct {
serverDetails *config.ServerDetails
releaseBundleName string
releaseBundleVersion string
- signingKeyName string
sync bool
rbProjectKey string
}
@@ -49,3 +50,21 @@ func validateArtifactoryVersionSupported(serverDetails *config.ServerDetails) er
return clientUtils.ValidateMinimumVersion(clientUtils.Artifactory, versionStr, minimalLifecycleArtifactoryVersion)
}
+
+// If distribution rules are empty, distribute to all edges.
+func getAggregatedDistRules(distributionRules *spec.DistributionRules) (aggregatedRules []*distribution.DistributionCommonParams) {
+ if isDistributionRulesEmpty(distributionRules) {
+ aggregatedRules = append(aggregatedRules, &distribution.DistributionCommonParams{SiteName: "*"})
+ } else {
+ for _, rules := range distributionRules.DistributionRules {
+ aggregatedRules = append(aggregatedRules, rules.ToDistributionCommonParams())
+ }
+ }
+ return
+}
+
+func isDistributionRulesEmpty(distributionRules *spec.DistributionRules) bool {
+ return distributionRules == nil ||
+ len(distributionRules.DistributionRules) == 0 ||
+ len(distributionRules.DistributionRules) == 1 && distributionRules.DistributionRules[0].IsEmpty()
+}
diff --git a/lifecycle/createcommon.go b/lifecycle/createcommon.go
index 4d5fba599..6b0e7f35c 100644
--- a/lifecycle/createcommon.go
+++ b/lifecycle/createcommon.go
@@ -1,11 +1,24 @@
package lifecycle
import (
+ "github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+ "github.com/jfrog/jfrog-client-go/utils/errorutils"
+)
+
+const (
+ missingCreationSourcesErrMsg = "unexpected err while validating spec - could not detect any creation sources"
+ multipleCreationSourcesErrMsg = "multiple creation sources were detected in separate spec files. Only a single creation source should be provided. Detected:"
+ singleAqlErrMsg = "only a single aql query can be provided"
)
type ReleaseBundleCreateCommand struct {
releaseBundleCmd
+ signingKeyName string
+ spec *spec.SpecFiles
+ // Backward compatibility:
buildsSpecPath string
releaseBundlesSpecPath string
}
@@ -44,11 +57,18 @@ func (rbc *ReleaseBundleCreateCommand) SetReleaseBundleProject(rbProjectKey stri
return rbc
}
+func (rbc *ReleaseBundleCreateCommand) SetSpec(spec *spec.SpecFiles) *ReleaseBundleCreateCommand {
+ rbc.spec = spec
+ return rbc
+}
+
+// Deprecated
func (rbc *ReleaseBundleCreateCommand) SetBuildsSpecPath(buildsSpecPath string) *ReleaseBundleCreateCommand {
rbc.buildsSpecPath = buildsSpecPath
return rbc
}
+// Deprecated
func (rbc *ReleaseBundleCreateCommand) SetReleaseBundlesSpecPath(releaseBundlesSpecPath string) *ReleaseBundleCreateCommand {
rbc.releaseBundlesSpecPath = releaseBundlesSpecPath
return rbc
@@ -72,8 +92,162 @@ func (rbc *ReleaseBundleCreateCommand) Run() error {
return err
}
- if rbc.buildsSpecPath != "" {
+ sourceType, err := rbc.identifySourceType()
+ if err != nil {
+ return err
+ }
+
+ switch sourceType {
+ case services.Aql:
+ return rbc.createFromAql(servicesManager, rbDetails, queryParams)
+ case services.Artifacts:
+ return rbc.createFromArtifacts(servicesManager, rbDetails, queryParams)
+ case services.Builds:
return rbc.createFromBuilds(servicesManager, rbDetails, queryParams)
+ case services.ReleaseBundles:
+ return rbc.createFromReleaseBundles(servicesManager, rbDetails, queryParams)
+ default:
+ return errorutils.CheckErrorf("unknown source for release bundle creation was provided")
+ }
+}
+
+func (rbc *ReleaseBundleCreateCommand) identifySourceType() (services.SourceType, error) {
+ switch {
+ case rbc.buildsSpecPath != "":
+ return services.Builds, nil
+ case rbc.releaseBundlesSpecPath != "":
+ return services.ReleaseBundles, nil
+ case rbc.spec != nil:
+ return validateAndIdentifyRbCreationSpec(rbc.spec.Files)
+ default:
+ return "", errorutils.CheckErrorf("a spec file input is mandatory")
+ }
+}
+
+func validateAndIdentifyRbCreationSpec(files []spec.File) (services.SourceType, error) {
+ if len(files) == 0 {
+ return "", errorutils.CheckErrorf("spec must include at least one file group")
+ }
+
+ var detectedCreationSources []services.SourceType
+ for _, file := range files {
+ sourceType, err := validateFile(file)
+ if err != nil {
+ return "", err
+ }
+ detectedCreationSources = append(detectedCreationSources, sourceType)
+ }
+
+ if err := validateCreationSources(detectedCreationSources); err != nil {
+ return "", err
+ }
+ return detectedCreationSources[0], nil
+}
+
+func validateCreationSources(detectedCreationSources []services.SourceType) error {
+ if len(detectedCreationSources) == 0 {
+ return errorutils.CheckErrorf(missingCreationSourcesErrMsg)
+ }
+
+ // Assert single creation source.
+ for i := 1; i < len(detectedCreationSources); i++ {
+ if detectedCreationSources[i] != detectedCreationSources[0] {
+ return generateSingleCreationSourceErr(detectedCreationSources)
+ }
+ }
+
+ // If aql, assert single file.
+ if detectedCreationSources[0] == services.Aql && len(detectedCreationSources) > 1 {
+ return errorutils.CheckErrorf(singleAqlErrMsg)
+ }
+ return nil
+}
+
+func generateSingleCreationSourceErr(detectedCreationSources []services.SourceType) error {
+ var detectedStr []string
+ for _, source := range detectedCreationSources {
+ detectedStr = append(detectedStr, string(source))
+ }
+ return errorutils.CheckErrorf(
+ "%s '%s'", multipleCreationSourcesErrMsg, coreutils.ListToText(detectedStr))
+}
+
+func validateFile(file spec.File) (services.SourceType, error) {
+ // Aql creation source:
+ isAql := len(file.Aql.ItemsFind) > 0
+
+ // Build creation source:
+ isBuild := len(file.Build) > 0
+ isIncludeDeps, _ := file.IsIncludeDeps(false)
+
+ // Bundle creation source:
+ isBundle := len(file.Bundle) > 0
+
+ // Build & bundle:
+ isProject := len(file.Project) > 0
+
+ // Artifacts creation source:
+ isPattern := len(file.Pattern) > 0
+ isExclusions := len(file.Exclusions) > 0 && len(file.Exclusions[0]) > 0
+ isProps := len(file.Props) > 0
+ isExcludeProps := len(file.ExcludeProps) > 0
+ isRecursive, err := file.IsRecursive(true)
+ if err != nil {
+ return "", errorutils.CheckErrorf("invalid value provided to the 'recursive' field. error: %s", err.Error())
+ }
+
+ // Unsupported:
+ isPathMapping := len(file.PathMapping.Input) > 0 || len(file.PathMapping.Output) > 0
+ isTarget := len(file.Target) > 0
+ isSortOrder := len(file.SortOrder) > 0
+ isSortBy := len(file.SortBy) > 0
+ isExcludeArtifacts, _ := file.IsExcludeArtifacts(false)
+ isGPGKey := len(file.PublicGpgKey) > 0
+ isOffset := file.Offset > 0
+ isLimit := file.Limit > 0
+ isArchive := len(file.Archive) > 0
+ isSymlinks, _ := file.IsSymlinks(false)
+ isRegexp := file.Regexp == "true"
+ isAnt := file.Ant == "true"
+ isExplode, _ := file.IsExplode(false)
+ isBypassArchiveInspection, _ := file.IsBypassArchiveInspection(false)
+ isTransitive, _ := file.IsTransitive(false)
+
+ if isPathMapping || isTarget || isSortOrder || isSortBy || isExcludeArtifacts || isGPGKey || isOffset || isLimit ||
+ isSymlinks || isArchive || isAnt || isRegexp || isExplode || isBypassArchiveInspection || isTransitive {
+ return "", errorutils.CheckErrorf("unsupported fields were provided in file spec. " +
+ "release bundle creation file spec only supports the following fields: " +
+ "'aql', 'build', 'includeDeps', 'bundle', 'project', 'pattern', 'exclusions', 'props', 'excludeProps' and 'recursive'")
+ }
+ if coreutils.SumTrueValues([]bool{isAql, isBuild, isBundle, isPattern}) != 1 {
+ return "", errorutils.CheckErrorf("exactly one creation source is supported (aql, builds, release bundles or pattern (artifacts))")
+ }
+
+ switch {
+ case isAql:
+ return services.Aql,
+ validateCreationSource([]bool{isIncludeDeps, isProject, isExclusions, isProps, isExcludeProps, !isRecursive},
+ "aql creation source supports no other fields")
+ case isBuild:
+ return services.Builds,
+ validateCreationSource([]bool{isExclusions, isProps, isExcludeProps, !isRecursive},
+ "builds creation source only supports the 'includeDeps' and 'project' fields")
+ case isBundle:
+ return services.ReleaseBundles,
+ validateCreationSource([]bool{isIncludeDeps, isExclusions, isProps, isExcludeProps, !isRecursive},
+ "release bundles creation source only supports the 'project' field")
+ case isPattern:
+ return services.Artifacts,
+ validateCreationSource([]bool{isIncludeDeps, isProject},
+ "release bundles creation source only supports the 'exclusions', 'props', 'excludeProps' and 'recursive' fields")
+ default:
+ return "", errorutils.CheckErrorf("unexpected err in spec validation")
+ }
+}
+
+func validateCreationSource(unsupportedFields []bool, errMsg string) error {
+ if coreutils.SumTrueValues(unsupportedFields) > 0 {
+ return errorutils.CheckErrorf(errMsg)
}
- return rbc.createFromReleaseBundles(servicesManager, rbDetails, queryParams)
+ return nil
}
diff --git a/lifecycle/createcommon_test.go b/lifecycle/createcommon_test.go
new file mode 100644
index 000000000..a0c3b598f
--- /dev/null
+++ b/lifecycle/createcommon_test.go
@@ -0,0 +1,76 @@
+package lifecycle
+
+import (
+ "github.com/jfrog/jfrog-cli-core/v2/common/spec"
+ "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestValidateCreationSources(t *testing.T) {
+ testCases := []struct {
+ testName string
+ detectedCreationSources []services.SourceType
+ errExpected bool
+ errMsg string
+ }{
+ {"missing creation sources", []services.SourceType{}, true, missingCreationSourcesErrMsg},
+ {"single creation source", []services.SourceType{services.Aql, services.Artifacts, services.Builds},
+ true, multipleCreationSourcesErrMsg + " 'aql, artifacts and builds'"},
+ {"single aql err", []services.SourceType{services.Aql, services.Aql}, true, singleAqlErrMsg},
+ {"valid aql", []services.SourceType{services.Aql}, false, ""},
+ {"valid artifacts", []services.SourceType{services.Artifacts, services.Artifacts}, false, ""},
+ {"valid builds", []services.SourceType{services.Builds, services.Builds}, false, ""},
+ {"valid release bundles", []services.SourceType{services.ReleaseBundles, services.ReleaseBundles}, false, ""},
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.testName, func(t *testing.T) {
+ err := validateCreationSources(testCase.detectedCreationSources)
+ if testCase.errExpected {
+ assert.EqualError(t, err, testCase.errMsg)
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestValidateFile(t *testing.T) {
+ testCases := []struct {
+ testName string
+ file spec.File
+ errExpected bool
+ expectedSourceType services.SourceType
+ }{
+ {"valid aql", spec.File{Aql: utils.Aql{ItemsFind: "abc"}}, false, services.Aql},
+ {"valid build", spec.File{Build: "name/number", IncludeDeps: "true", Project: "project"}, false, services.Builds},
+ {"valid bundle", spec.File{Bundle: "name/number", Project: "project"}, false, services.ReleaseBundles},
+ {"valid artifacts",
+ spec.File{
+ Pattern: "repo/path/file",
+ Exclusions: []string{"exclude"},
+ Props: "prop",
+ ExcludeProps: "exclude prop",
+ Recursive: "false"}, false, services.Artifacts},
+ {"invalid fields", spec.File{PathMapping: utils.PathMapping{Input: "input"}, Target: "target"}, true, ""},
+ {"multiple creation sources",
+ spec.File{Aql: utils.Aql{ItemsFind: "abc"}, Build: "name/number", Bundle: "name/number", Pattern: "repo/path/file"},
+ true, ""},
+ {"invalid aql", spec.File{Aql: utils.Aql{ItemsFind: "abc"}, Props: "prop"}, true, ""},
+ {"invalid builds", spec.File{Build: "name/number", Recursive: "false"}, true, ""},
+ {"invalid bundles", spec.File{Bundle: "name/number", IncludeDeps: "true"}, true, ""},
+ {"invalid artifacts", spec.File{Pattern: "repo/path/file", Project: "proj"}, true, ""},
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.testName, func(t *testing.T) {
+ sourceType, err := validateFile(testCase.file)
+ if testCase.errExpected {
+ assert.Error(t, err)
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, testCase.expectedSourceType, sourceType)
+ }
+ })
+ }
+}
diff --git a/lifecycle/createfromaql.go b/lifecycle/createfromaql.go
new file mode 100644
index 000000000..12918bd75
--- /dev/null
+++ b/lifecycle/createfromaql.go
@@ -0,0 +1,13 @@
+package lifecycle
+
+import (
+ "fmt"
+ "github.com/jfrog/jfrog-client-go/lifecycle"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+)
+
+func (rbc *ReleaseBundleCreateCommand) createFromAql(servicesManager *lifecycle.LifecycleServicesManager,
+ rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error {
+ aqlQuery := fmt.Sprintf(`items.find(%s)`, rbc.spec.Get(0).Aql.ItemsFind)
+ return servicesManager.CreateReleaseBundleFromAql(rbDetails, queryParams, rbc.signingKeyName, aqlQuery)
+}
diff --git a/lifecycle/createfromartifacts.go b/lifecycle/createfromartifacts.go
new file mode 100644
index 000000000..8a2633451
--- /dev/null
+++ b/lifecycle/createfromartifacts.go
@@ -0,0 +1,55 @@
+package lifecycle
+
+import (
+ "errors"
+ "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
+ rtServicesUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
+ "github.com/jfrog/jfrog-client-go/lifecycle"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+ "github.com/jfrog/jfrog-client-go/utils/io/content"
+ "github.com/jfrog/jfrog-client-go/utils/log"
+ "path"
+)
+
+func (rbc *ReleaseBundleCreateCommand) createFromArtifacts(lcServicesManager *lifecycle.LifecycleServicesManager,
+ rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) (err error) {
+
+ rtServicesManager, err := utils.CreateServiceManager(rbc.serverDetails, 3, 0, false)
+ if err != nil {
+ return err
+ }
+
+ log.Info("Searching artifacts...")
+ searchResults, callbackFunc, err := utils.SearchFiles(rtServicesManager, rbc.spec)
+ defer func() {
+ err = errors.Join(err, callbackFunc())
+ }()
+ if err != nil {
+ return err
+ }
+ artifactsSource, err := aqlResultToArtifactsSource(searchResults)
+ if err != nil {
+ return err
+ }
+
+ return lcServicesManager.CreateReleaseBundleFromArtifacts(rbDetails, queryParams, rbc.signingKeyName, artifactsSource)
+}
+
+func aqlResultToArtifactsSource(readers []*content.ContentReader) (artifactsSource services.CreateFromArtifacts, err error) {
+ for _, reader := range readers {
+ for searchResult := new(rtServicesUtils.ResultItem); reader.NextRecord(searchResult) == nil; searchResult = new(rtServicesUtils.ResultItem) {
+ if err != nil {
+ return
+ }
+ artifactsSource.Artifacts = append(artifactsSource.Artifacts, services.ArtifactSource{
+ Path: path.Join(searchResult.Repo, searchResult.Path, searchResult.Name),
+ Sha256: searchResult.Sha256,
+ })
+ }
+ if err = reader.GetError(); err != nil {
+ return
+ }
+ reader.Reset()
+ }
+ return
+}
diff --git a/lifecycle/createfrombuilds.go b/lifecycle/createfrombuilds.go
index a955b997a..02e54e644 100644
--- a/lifecycle/createfrombuilds.go
+++ b/lifecycle/createfrombuilds.go
@@ -3,6 +3,7 @@ package lifecycle
import (
"encoding/json"
rtUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
+ "github.com/jfrog/jfrog-cli-core/v2/common/spec"
rtServices "github.com/jfrog/jfrog-client-go/artifactory/services"
"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"github.com/jfrog/jfrog-client-go/lifecycle"
@@ -14,27 +15,38 @@ import (
func (rbc *ReleaseBundleCreateCommand) createFromBuilds(servicesManager *lifecycle.LifecycleServicesManager,
rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error {
- builds := CreateFromBuildsSpec{}
- content, err := fileutils.ReadFile(rbc.buildsSpecPath)
+ var buildsSource services.CreateFromBuildsSource
+ var err error
+ if rbc.buildsSpecPath != "" {
+ buildsSource, err = rbc.getBuildSourceFromBuildsSpec()
+ } else {
+ buildsSource, err = rbc.convertSpecToBuildsSource(rbc.spec.Files)
+ }
if err != nil {
return err
}
- if err = json.Unmarshal(content, &builds); err != nil {
- return errorutils.CheckError(err)
- }
- if len(builds.Builds) == 0 {
+ if len(buildsSource.Builds) == 0 {
return errorutils.CheckErrorf("at least one build is expected in order to create a release bundle from builds")
}
- buildsSource, err := rbc.convertToBuildsSource(builds)
+ return servicesManager.CreateReleaseBundleFromBuilds(rbDetails, queryParams, rbc.signingKeyName, buildsSource)
+}
+
+func (rbc *ReleaseBundleCreateCommand) getBuildSourceFromBuildsSpec() (buildsSource services.CreateFromBuildsSource, err error) {
+ builds := CreateFromBuildsSpec{}
+ content, err := fileutils.ReadFile(rbc.buildsSpecPath)
if err != nil {
- return err
+ return
}
- return servicesManager.CreateReleaseBundleFromBuilds(rbDetails, queryParams, rbc.signingKeyName, buildsSource)
+ if err = json.Unmarshal(content, &builds); errorutils.CheckError(err) != nil {
+ return
+ }
+
+ return rbc.convertBuildsSpecToBuildsSource(builds)
}
-func (rbc *ReleaseBundleCreateCommand) convertToBuildsSource(builds CreateFromBuildsSpec) (services.CreateFromBuildsSource, error) {
+func (rbc *ReleaseBundleCreateCommand) convertBuildsSpecToBuildsSource(builds CreateFromBuildsSpec) (services.CreateFromBuildsSource, error) {
buildsSource := services.CreateFromBuildsSource{}
for _, build := range builds.Builds {
buildSource := services.BuildSource{BuildName: build.Name, IncludeDependencies: build.IncludeDependencies}
@@ -49,6 +61,29 @@ func (rbc *ReleaseBundleCreateCommand) convertToBuildsSource(builds CreateFromBu
return buildsSource, nil
}
+func (rbc *ReleaseBundleCreateCommand) convertSpecToBuildsSource(files []spec.File) (services.CreateFromBuildsSource, error) {
+ buildsSource := services.CreateFromBuildsSource{}
+ for _, file := range files {
+ buildName, buildNumber, err := rbc.getBuildDetailsFromIdentifier(file.Build, file.Project)
+ if err != nil {
+ return services.CreateFromBuildsSource{}, err
+ }
+ isIncludeDeps, err := file.IsIncludeDeps(false)
+ if err != nil {
+ return services.CreateFromBuildsSource{}, err
+ }
+
+ buildSource := services.BuildSource{
+ BuildName: buildName,
+ BuildNumber: buildNumber,
+ BuildRepository: utils.GetBuildInfoRepositoryByProject(file.Project),
+ IncludeDependencies: isIncludeDeps,
+ }
+ buildsSource.Builds = append(buildsSource.Builds, buildSource)
+ }
+ return buildsSource, nil
+}
+
func (rbc *ReleaseBundleCreateCommand) getLatestBuildNumberIfEmpty(buildName, buildNumber, project string) (string, error) {
if buildNumber != "" {
return buildNumber, nil
@@ -69,6 +104,22 @@ func (rbc *ReleaseBundleCreateCommand) getLatestBuildNumberIfEmpty(buildName, bu
return buildNumber, nil
}
+func (rbc *ReleaseBundleCreateCommand) getBuildDetailsFromIdentifier(buildIdentifier, project string) (string, string, error) {
+ aqlService, err := rbc.getAqlService()
+ if err != nil {
+ return "", "", err
+ }
+
+ buildName, buildNumber, err := utils.GetBuildNameAndNumberFromBuildIdentifier(buildIdentifier, project, aqlService)
+ if err != nil {
+ return "", "", err
+ }
+ if buildName == "" || buildNumber == "" {
+ return "", "", errorutils.CheckErrorf("could not identify a build info by the '%s' identifier in artifactory", buildIdentifier)
+ }
+ return buildName, buildNumber, nil
+}
+
func (rbc *ReleaseBundleCreateCommand) getAqlService() (*rtServices.AqlService, error) {
rtServiceManager, err := rtUtils.CreateServiceManager(rbc.serverDetails, 3, 0, false)
if err != nil {
diff --git a/lifecycle/createfrombundles.go b/lifecycle/createfrombundles.go
index 1e9253351..7c18141de 100644
--- a/lifecycle/createfrombundles.go
+++ b/lifecycle/createfrombundles.go
@@ -2,6 +2,8 @@ package lifecycle
import (
"encoding/json"
+ "github.com/jfrog/jfrog-cli-core/v2/common/spec"
+ "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"github.com/jfrog/jfrog-client-go/lifecycle"
"github.com/jfrog/jfrog-client-go/lifecycle/services"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
@@ -11,20 +13,21 @@ import (
func (rbc *ReleaseBundleCreateCommand) createFromReleaseBundles(servicesManager *lifecycle.LifecycleServicesManager,
rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error {
- bundles := CreateFromReleaseBundlesSpec{}
- content, err := fileutils.ReadFile(rbc.releaseBundlesSpecPath)
+ var releaseBundlesSource services.CreateFromReleaseBundlesSource
+ var err error
+ if rbc.releaseBundlesSpecPath != "" {
+ releaseBundlesSource, err = rbc.getReleaseBundlesSourceFromBundlesSpec()
+ } else {
+ releaseBundlesSource, err = rbc.convertSpecToReleaseBundlesSource(rbc.spec.Files)
+ }
if err != nil {
return err
}
- if err = json.Unmarshal(content, &bundles); err != nil {
- return errorutils.CheckError(err)
- }
- if len(bundles.ReleaseBundles) == 0 {
+ if len(releaseBundlesSource.ReleaseBundles) == 0 {
return errorutils.CheckErrorf("at least one release bundle is expected in order to create a release bundle from release bundles")
}
- releaseBundlesSource := rbc.convertToReleaseBundlesSource(bundles)
return servicesManager.CreateReleaseBundleFromBundles(rbDetails, queryParams, rbc.signingKeyName, releaseBundlesSource)
}
@@ -41,6 +44,40 @@ func (rbc *ReleaseBundleCreateCommand) convertToReleaseBundlesSource(bundles Cre
return releaseBundlesSource
}
+func (rbc *ReleaseBundleCreateCommand) convertSpecToReleaseBundlesSource(files []spec.File) (services.CreateFromReleaseBundlesSource, error) {
+ releaseBundlesSource := services.CreateFromReleaseBundlesSource{}
+ for _, file := range files {
+ name, version, err := utils.ParseNameAndVersion(file.Bundle, false)
+ if err != nil {
+ return releaseBundlesSource, err
+ }
+ if name == "" || version == "" {
+ return releaseBundlesSource, errorutils.CheckErrorf(
+ "invalid release bundle source was provided. Both name and version are mandatory. Provided name: '%s', version: '%s'", name, version)
+ }
+ rbSource := services.ReleaseBundleSource{
+ ReleaseBundleName: name,
+ ReleaseBundleVersion: version,
+ ProjectKey: file.Project,
+ }
+ releaseBundlesSource.ReleaseBundles = append(releaseBundlesSource.ReleaseBundles, rbSource)
+ }
+ return releaseBundlesSource, nil
+}
+
+func (rbc *ReleaseBundleCreateCommand) getReleaseBundlesSourceFromBundlesSpec() (releaseBundlesSource services.CreateFromReleaseBundlesSource, err error) {
+ releaseBundles := CreateFromReleaseBundlesSpec{}
+ content, err := fileutils.ReadFile(rbc.releaseBundlesSpecPath)
+ if err != nil {
+ return
+ }
+ if err = json.Unmarshal(content, &releaseBundles); errorutils.CheckError(err) != nil {
+ return
+ }
+
+ return rbc.convertToReleaseBundlesSource(releaseBundles), nil
+}
+
type CreateFromReleaseBundlesSpec struct {
ReleaseBundles []SourceReleaseBundleSpec `json:"releaseBundles,omitempty"`
}
diff --git a/lifecycle/deletelocal.go b/lifecycle/deletelocal.go
new file mode 100644
index 000000000..d1e434aa8
--- /dev/null
+++ b/lifecycle/deletelocal.go
@@ -0,0 +1,132 @@
+package lifecycle
+
+import (
+ "errors"
+ "fmt"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/config"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
+ "github.com/jfrog/jfrog-client-go/lifecycle"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+ "github.com/jfrog/jfrog-client-go/utils/log"
+ "strings"
+)
+
+type ReleaseBundleDeleteCommand struct {
+ releaseBundleCmd
+ environment string
+ quiet bool
+}
+
+func NewReleaseBundleDeleteCommand() *ReleaseBundleDeleteCommand {
+ return &ReleaseBundleDeleteCommand{}
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetServerDetails(serverDetails *config.ServerDetails) *ReleaseBundleDeleteCommand {
+ rbd.serverDetails = serverDetails
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetReleaseBundleName(releaseBundleName string) *ReleaseBundleDeleteCommand {
+ rbd.releaseBundleName = releaseBundleName
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetReleaseBundleVersion(releaseBundleVersion string) *ReleaseBundleDeleteCommand {
+ rbd.releaseBundleVersion = releaseBundleVersion
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetSync(sync bool) *ReleaseBundleDeleteCommand {
+ rbd.sync = sync
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetReleaseBundleProject(rbProjectKey string) *ReleaseBundleDeleteCommand {
+ rbd.rbProjectKey = rbProjectKey
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetEnvironment(environment string) *ReleaseBundleDeleteCommand {
+ rbd.environment = environment
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) SetQuiet(quiet bool) *ReleaseBundleDeleteCommand {
+ rbd.quiet = quiet
+ return rbd
+}
+
+func (rbd *ReleaseBundleDeleteCommand) CommandName() string {
+ return "rb_delete"
+}
+
+func (rbd *ReleaseBundleDeleteCommand) ServerDetails() (*config.ServerDetails, error) {
+ return rbd.serverDetails, nil
+}
+
+func (rbd *ReleaseBundleDeleteCommand) Run() error {
+ if err := validateArtifactoryVersionSupported(rbd.serverDetails); err != nil {
+ return err
+ }
+
+ servicesManager, rbDetails, queryParams, err := rbd.getPrerequisites()
+ if err != nil {
+ return err
+ }
+
+ if rbd.environment != "" {
+ return rbd.deletePromotionsOnly(servicesManager, rbDetails, queryParams)
+ }
+ return rbd.deleteLocalReleaseBundle(servicesManager, rbDetails, queryParams)
+}
+
+func (rbd *ReleaseBundleDeleteCommand) deletePromotionsOnly(servicesManager *lifecycle.LifecycleServicesManager,
+ rbDetails services.ReleaseBundleDetails, commonQueryParams services.CommonOptionalQueryParams) error {
+
+ deletionSubject := fmt.Sprintf("all promotions to environment '%s' of release bundle '%s/%s'", rbd.environment, rbd.releaseBundleName, rbd.releaseBundleVersion)
+ if !rbd.confirmDelete(deletionSubject) {
+ return nil
+ }
+
+ optionalQueryParams := services.GetPromotionsOptionalQueryParams{ProjectKey: commonQueryParams.ProjectKey}
+ response, err := servicesManager.GetReleaseBundleVersionPromotions(rbDetails, optionalQueryParams)
+ if err != nil {
+ return err
+ }
+ success := 0
+ fail := 0
+ for _, promotion := range response.Promotions {
+ if strings.EqualFold(promotion.Environment, rbd.environment) {
+ if curErr := servicesManager.DeleteReleaseBundleVersionPromotion(rbDetails, commonQueryParams, promotion.CreatedMillis.String()); curErr != nil {
+ err = errors.Join(err, curErr)
+ fail++
+ } else {
+ success++
+ }
+ }
+ }
+ if success == 0 && fail == 0 {
+ log.Info(fmt.Sprintf("No promotions were found for environment '%s'", rbd.environment))
+ } else {
+ log.Info(fmt.Sprintf("Promotions deleted successfully: %d, failed: %d", success, fail))
+ }
+
+ return err
+}
+
+func (rbd *ReleaseBundleDeleteCommand) deleteLocalReleaseBundle(servicesManager *lifecycle.LifecycleServicesManager,
+ rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error {
+ deletionSubject := fmt.Sprintf("release bundle '%s/%s' locally with all its promotions", rbd.releaseBundleName, rbd.releaseBundleVersion)
+ if !rbd.confirmDelete(deletionSubject) {
+ return nil
+ }
+ return servicesManager.DeleteReleaseBundleVersion(rbDetails, queryParams)
+}
+
+func (rbd *ReleaseBundleDeleteCommand) confirmDelete(deletionSubject string) bool {
+ if rbd.quiet {
+ return true
+ }
+ return coreutils.AskYesNo(
+ fmt.Sprintf("Are you sure you want to delete %s?\n"+avoidConfirmationMsg, deletionSubject), false)
+}
diff --git a/lifecycle/deleteremote.go b/lifecycle/deleteremote.go
new file mode 100644
index 000000000..6b1665131
--- /dev/null
+++ b/lifecycle/deleteremote.go
@@ -0,0 +1,159 @@
+package lifecycle
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/jfrog/jfrog-cli-core/v2/common/spec"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/config"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
+ "github.com/jfrog/jfrog-client-go/lifecycle"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+ clientutils "github.com/jfrog/jfrog-client-go/utils"
+ "github.com/jfrog/jfrog-client-go/utils/distribution"
+ "github.com/jfrog/jfrog-client-go/utils/errorutils"
+ "github.com/jfrog/jfrog-client-go/utils/log"
+)
+
+const avoidConfirmationMsg = "You can avoid this confirmation message by adding --quiet to the command."
+
+type ReleaseBundleRemoteDeleteCommand struct {
+ releaseBundleCmd
+ distributionRules *spec.DistributionRules
+ dryRun bool
+ quiet bool
+ maxWaitMinutes int
+}
+
+func NewReleaseBundleRemoteDeleteCommand() *ReleaseBundleRemoteDeleteCommand {
+ return &ReleaseBundleRemoteDeleteCommand{}
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetServerDetails(serverDetails *config.ServerDetails) *ReleaseBundleRemoteDeleteCommand {
+ rbd.serverDetails = serverDetails
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetReleaseBundleName(releaseBundleName string) *ReleaseBundleRemoteDeleteCommand {
+ rbd.releaseBundleName = releaseBundleName
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetReleaseBundleVersion(releaseBundleVersion string) *ReleaseBundleRemoteDeleteCommand {
+ rbd.releaseBundleVersion = releaseBundleVersion
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetSync(sync bool) *ReleaseBundleRemoteDeleteCommand {
+ rbd.sync = sync
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetReleaseBundleProject(rbProjectKey string) *ReleaseBundleRemoteDeleteCommand {
+ rbd.rbProjectKey = rbProjectKey
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetDistributionRules(distributionRules *spec.DistributionRules) *ReleaseBundleRemoteDeleteCommand {
+ rbd.distributionRules = distributionRules
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetDryRun(dryRun bool) *ReleaseBundleRemoteDeleteCommand {
+ rbd.dryRun = dryRun
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetQuiet(quiet bool) *ReleaseBundleRemoteDeleteCommand {
+ rbd.quiet = quiet
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) SetMaxWaitMinutes(maxWaitMinutes int) *ReleaseBundleRemoteDeleteCommand {
+ rbd.maxWaitMinutes = maxWaitMinutes
+ return rbd
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) CommandName() string {
+ return "rb_remote_delete"
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) ServerDetails() (*config.ServerDetails, error) {
+ return rbd.serverDetails, nil
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) Run() error {
+ if err := validateArtifactoryVersionSupported(rbd.serverDetails); err != nil {
+ return err
+ }
+
+ servicesManager, rbDetails, queryParams, err := rbd.getPrerequisites()
+ if err != nil {
+ return err
+ }
+
+ return rbd.deleteRemote(servicesManager, rbDetails, queryParams)
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) deleteRemote(servicesManager *lifecycle.LifecycleServicesManager,
+ rbDetails services.ReleaseBundleDetails, queryParams services.CommonOptionalQueryParams) error {
+
+ confirm, err := rbd.confirmDelete()
+ if err != nil || !confirm {
+ return err
+ }
+
+ aggregatedRules := rbd.getAggregatedDistRules()
+
+ return servicesManager.RemoteDeleteReleaseBundle(rbDetails, services.ReleaseBundleRemoteDeleteParams{
+ DistributionRules: aggregatedRules,
+ DryRun: rbd.dryRun,
+ MaxWaitMinutes: rbd.maxWaitMinutes,
+ CommonOptionalQueryParams: queryParams,
+ })
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) distributionRulesEmpty() bool {
+ return rbd.distributionRules == nil ||
+ len(rbd.distributionRules.DistributionRules) == 0 ||
+ len(rbd.distributionRules.DistributionRules) == 1 && rbd.distributionRules.DistributionRules[0].IsEmpty()
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) confirmDelete() (bool, error) {
+ if rbd.quiet {
+ return true, nil
+ }
+
+ message := fmt.Sprintf("Are you sure you want to delete the release bundle '%s/%s' remotely ", rbd.releaseBundleName, rbd.releaseBundleVersion)
+ if rbd.distributionRulesEmpty() {
+ message += "from all edges?"
+ } else {
+ var distributionRulesBodies []distribution.DistributionRulesBody
+ for _, rule := range rbd.distributionRules.DistributionRules {
+ distributionRulesBodies = append(distributionRulesBodies, distribution.DistributionRulesBody{
+ SiteName: rule.SiteName,
+ CityName: rule.CityName,
+ CountryCodes: rule.CountryCodes,
+ })
+ }
+ bytes, err := json.Marshal(distributionRulesBodies)
+ if err != nil {
+ return false, errorutils.CheckError(err)
+ }
+
+ log.Output(clientutils.IndentJson(bytes))
+ message += "from all edges with the above distribution rules?"
+ }
+
+ return coreutils.AskYesNo(message+"\n"+avoidConfirmationMsg, false), nil
+}
+
+func (rbd *ReleaseBundleRemoteDeleteCommand) getAggregatedDistRules() (aggregatedRules []*distribution.DistributionCommonParams) {
+ if rbd.distributionRulesEmpty() {
+ aggregatedRules = append(aggregatedRules, &distribution.DistributionCommonParams{SiteName: "*"})
+ } else {
+ for _, rules := range rbd.distributionRules.DistributionRules {
+ aggregatedRules = append(aggregatedRules, rules.ToDistributionCommonParams())
+ }
+ }
+ return
+}
diff --git a/lifecycle/distribute.go b/lifecycle/distribute.go
index 76a187330..76165ef56 100644
--- a/lifecycle/distribute.go
+++ b/lifecycle/distribute.go
@@ -1,21 +1,19 @@
package lifecycle
import (
- "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/lifecycle/services"
- "github.com/jfrog/jfrog-client-go/utils/distribution"
)
type ReleaseBundleDistributeCommand struct {
- serverDetails *config.ServerDetails
- distributeBundlesParams distribution.DistributionParams
- distributionRules *spec.DistributionRules
- dryRun bool
- autoCreateRepo bool
- pathMappingPattern string
- pathMappingTarget string
+ releaseBundleCmd
+ distributionRules *spec.DistributionRules
+ dryRun bool
+ autoCreateRepo bool
+ pathMappingPattern string
+ pathMappingTarget string
+ maxWaitMinutes int
}
func NewReleaseBundleDistributeCommand() *ReleaseBundleDistributeCommand {
@@ -27,8 +25,18 @@ func (rbd *ReleaseBundleDistributeCommand) SetServerDetails(serverDetails *confi
return rbd
}
-func (rbd *ReleaseBundleDistributeCommand) SetDistributeBundleParams(params distribution.DistributionParams) *ReleaseBundleDistributeCommand {
- rbd.distributeBundlesParams = params
+func (rbd *ReleaseBundleDistributeCommand) SetReleaseBundleName(releaseBundleName string) *ReleaseBundleDistributeCommand {
+ rbd.releaseBundleName = releaseBundleName
+ return rbd
+}
+
+func (rbd *ReleaseBundleDistributeCommand) SetReleaseBundleVersion(releaseBundleVersion string) *ReleaseBundleDistributeCommand {
+ rbd.releaseBundleVersion = releaseBundleVersion
+ return rbd
+}
+
+func (rbd *ReleaseBundleDistributeCommand) SetReleaseBundleProject(rbProjectKey string) *ReleaseBundleDistributeCommand {
+ rbd.rbProjectKey = rbProjectKey
return rbd
}
@@ -57,33 +65,41 @@ func (rbd *ReleaseBundleDistributeCommand) SetPathMappingTarget(pathMappingTarge
return rbd
}
+func (rbd *ReleaseBundleDistributeCommand) SetSync(sync bool) *ReleaseBundleDistributeCommand {
+ rbd.sync = sync
+ return rbd
+}
+
+func (rbd *ReleaseBundleDistributeCommand) SetMaxWaitMinutes(maxWaitMinutes int) *ReleaseBundleDistributeCommand {
+ rbd.maxWaitMinutes = maxWaitMinutes
+ return rbd
+}
+
func (rbd *ReleaseBundleDistributeCommand) Run() error {
if err := validateArtifactoryVersionSupported(rbd.serverDetails); err != nil {
return err
}
- servicesManager, err := utils.CreateLifecycleServiceManager(rbd.serverDetails, rbd.dryRun)
+ servicesManager, rbDetails, _, err := rbd.getPrerequisites()
if err != nil {
return err
}
- for _, rule := range rbd.distributionRules.DistributionRules {
- rbd.distributeBundlesParams.DistributionRules = append(rbd.distributeBundlesParams.DistributionRules, rule.ToDistributionCommonParams())
- }
-
pathMapping := services.PathMapping{
Pattern: rbd.pathMappingPattern,
Target: rbd.pathMappingTarget,
}
- return servicesManager.DistributeReleaseBundle(services.ReleaseBundleDetails{
- ReleaseBundleName: rbd.distributeBundlesParams.Name,
- ReleaseBundleVersion: rbd.distributeBundlesParams.Version,
- }, services.DistributeReleaseBundleParams{
+ distributeParams := services.DistributeReleaseBundleParams{
+ Sync: rbd.sync,
AutoCreateRepo: rbd.autoCreateRepo,
- DistributionRules: rbd.distributeBundlesParams.DistributionRules,
+ MaxWaitMinutes: rbd.maxWaitMinutes,
+ DistributionRules: getAggregatedDistRules(rbd.distributionRules),
PathMappings: []services.PathMapping{pathMapping},
- })
+ ProjectKey: rbd.rbProjectKey,
+ }
+
+ return servicesManager.DistributeReleaseBundle(rbDetails, distributeParams)
}
func (rbd *ReleaseBundleDistributeCommand) ServerDetails() (*config.ServerDetails, error) {
diff --git a/lifecycle/export.go b/lifecycle/export.go
new file mode 100644
index 000000000..41001e27f
--- /dev/null
+++ b/lifecycle/export.go
@@ -0,0 +1,133 @@
+package lifecycle
+
+import (
+ artUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/config"
+ "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
+ "github.com/jfrog/jfrog-client-go/artifactory"
+ artServices "github.com/jfrog/jfrog-client-go/artifactory/services"
+ "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
+ clientConfig "github.com/jfrog/jfrog-client-go/config"
+ "github.com/jfrog/jfrog-client-go/lifecycle/services"
+ "github.com/jfrog/jfrog-client-go/utils/errorutils"
+ "github.com/jfrog/jfrog-client-go/utils/log"
+ "strings"
+)
+
+type ReleaseBundleExportCommand struct {
+ releaseBundleCmd
+ modifications services.Modifications
+ downloadConfigurations artUtils.DownloadConfiguration
+ targetPath string
+}
+
+func (rbe *ReleaseBundleExportCommand) Run() (err error) {
+ if err = validateArtifactoryVersionSupported(rbe.serverDetails); err != nil {
+ return
+ }
+ servicesManager, rbDetails, queryParams, err := rbe.getPrerequisites()
+ if err != nil {
+ return errorutils.CheckErrorf("Failed getting prerequisites for exporting command, error: '%s'", err.Error())
+ }
+ // Start the Export process and wait for completion
+ log.Info("Exporting Release Bundle archive...")
+ exportResponse, err := servicesManager.ExportReleaseBundle(rbDetails, rbe.modifications, queryParams)
+ if err != nil {
+ return errorutils.CheckErrorf("Failed exporting release bundle, error: '%s'", err.Error())
+ }
+ // Download the exported bundle
+ log.Debug("Downloading the exported bundle...")
+ downloaded, failed, err := rbe.downloadReleaseBundle(exportResponse, rbe.downloadConfigurations)
+ if err != nil || failed > 0 || downloaded < 1 {
+ return
+ }
+ log.Info("Successfully Downloaded Release Bundle archive")
+ return
+}
+
+// Download the exported release bundle using artifactory service manager
+func (rbe *ReleaseBundleExportCommand) downloadReleaseBundle(exportResponse services.ReleaseBundleExportedStatusResponse, downloadConfiguration artUtils.DownloadConfiguration) (downloaded int, failed int, err error) {
+ downloadParams := artServices.DownloadParams{
+ CommonParams: &utils.CommonParams{
+ Pattern: strings.TrimPrefix(exportResponse.RelativeUrl, "/"),
+ Target: rbe.targetPath,
+ },
+ MinSplitSize: downloadConfiguration.MinSplitSize,
+ SplitCount: downloadConfiguration.SplitCount,
+ }
+ artifactoryServiceManager, err := createArtifactoryServiceManager(rbe.serverDetails)
+ if err != nil {
+ return
+ }
+ return artifactoryServiceManager.DownloadFiles(downloadParams)
+
+}
+func (rbe *ReleaseBundleExportCommand) ServerDetails() (*config.ServerDetails, error) {
+ return rbe.serverDetails, nil
+}
+
+func (rbe *ReleaseBundleExportCommand) CommandName() string {
+ return "rb_export"
+}
+
+func NewReleaseBundleExportCommand() *ReleaseBundleExportCommand {
+ return &ReleaseBundleExportCommand{}
+}
+func (rbe *ReleaseBundleExportCommand) SetServerDetails(serverDetails *config.ServerDetails) *ReleaseBundleExportCommand {
+ rbe.serverDetails = serverDetails
+ return rbe
+}
+
+func (rbe *ReleaseBundleExportCommand) SetReleaseBundleExportModifications(modifications services.Modifications) *ReleaseBundleExportCommand {
+ rbe.modifications = modifications
+ return rbe
+}
+func (rbe *ReleaseBundleExportCommand) SetReleaseBundleName(releaseBundleName string) *ReleaseBundleExportCommand {
+ rbe.releaseBundleName = releaseBundleName
+ return rbe
+}
+
+func (rbe *ReleaseBundleExportCommand) SetReleaseBundleVersion(releaseBundleVersion string) *ReleaseBundleExportCommand {
+ rbe.releaseBundleVersion = releaseBundleVersion
+ return rbe
+}
+
+func (rbe *ReleaseBundleExportCommand) SetProject(project string) *ReleaseBundleExportCommand {
+ rbe.rbProjectKey = project
+ return rbe
+}
+
+func (rbe *ReleaseBundleExportCommand) SetDownloadConfiguration(downloadConfig artUtils.DownloadConfiguration) *ReleaseBundleExportCommand {
+ rbe.downloadConfigurations = downloadConfig
+ return rbe
+}
+
+func (rbe *ReleaseBundleExportCommand) SetTargetPath(target string) *ReleaseBundleExportCommand {
+ if target == "" {
+ // Default value as current dir
+ target += "./"
+ }
+ rbe.targetPath = target
+ return rbe
+}
+
+func createArtifactoryServiceManager(artDetails *config.ServerDetails) (artifactory.ArtifactoryServicesManager, error) {
+ certsPath, err := coreutils.GetJfrogCertsDir()
+ if err != nil {
+ return nil, err
+ }
+ artAuth, err := artDetails.CreateArtAuthConfig()
+ if err != nil {
+ return nil, err
+ }
+ serviceConfig, err := clientConfig.NewConfigBuilder().
+ SetServiceDetails(artAuth).
+ SetCertificatesPath(certsPath).
+ SetInsecureTls(artDetails.InsecureTls).
+ SetDryRun(false).
+ Build()
+ if err != nil {
+ return nil, err
+ }
+ return artifactory.New(serviceConfig)
+}
diff --git a/lifecycle/promote.go b/lifecycle/promote.go
index 0cf8b64d0..fb078ff36 100644
--- a/lifecycle/promote.go
+++ b/lifecycle/promote.go
@@ -10,6 +10,7 @@ import (
type ReleaseBundlePromoteCommand struct {
releaseBundleCmd
+ signingKeyName string
environment string
includeReposPatterns []string
excludeReposPatterns []string
diff --git a/tests/testdata/npm-workspaces/module1/package.json b/tests/testdata/npm-workspaces/module1/package.json
new file mode 100644
index 000000000..c98ade642
--- /dev/null
+++ b/tests/testdata/npm-workspaces/module1/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "module1",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/tests/testdata/npm-workspaces/module2/package.json b/tests/testdata/npm-workspaces/module2/package.json
new file mode 100644
index 000000000..d584476e5
--- /dev/null
+++ b/tests/testdata/npm-workspaces/module2/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "module2",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/tests/testdata/npm-workspaces/package.json b/tests/testdata/npm-workspaces/package.json
new file mode 100644
index 000000000..59c936a79
--- /dev/null
+++ b/tests/testdata/npm-workspaces/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "npm-pack-test",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "workspaces": ["module1","module2"]
+}
diff --git a/utils/config/encryption.go b/utils/config/encryption.go
index 3e040c93d..eb4db4445 100644
--- a/utils/config/encryption.go
+++ b/utils/config/encryption.go
@@ -5,6 +5,7 @@ import (
"crypto/cipher"
"crypto/rand"
"encoding/base64"
+ ioutils "github.com/jfrog/gofrog/io"
"io"
"os"
"strconv"
@@ -115,12 +116,7 @@ func getEncryptionKeyFromSecurityConfFile() (key string, err error) {
config := viper.New()
config.SetConfigType("yaml")
f, err := os.Open(secFile)
- defer func() {
- e := f.Close()
- if err == nil {
- err = e
- }
- }()
+ defer ioutils.Close(f, &err)
if err != nil {
return "", errorutils.CheckError(err)
}
diff --git a/utils/config/tokenrefresh.go b/utils/config/tokenrefresh.go
index 298a3917d..e8f4da70b 100644
--- a/utils/config/tokenrefresh.go
+++ b/utils/config/tokenrefresh.go
@@ -1,6 +1,7 @@
package config
import (
+ "errors"
"github.com/jfrog/jfrog-client-go/access"
accessservices "github.com/jfrog/jfrog-client-go/access/services"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
@@ -75,10 +76,7 @@ func tokenRefreshHandler(currentAccessToken string, tokenType TokenType) (newAcc
unlockFunc, err := lock.CreateLock(lockDirPath)
// Defer the lockFile.Unlock() function before throwing a possible error to avoid deadlock situations.
defer func() {
- e := unlockFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, unlockFunc())
}()
if err != nil {
return
@@ -211,10 +209,7 @@ func CreateInitialRefreshableTokensIfNeeded(serverDetails *ServerDetails) (err e
unlockFunc, err := lock.CreateLock(lockDirPath)
// Defer the lockFile.Unlock() function before throwing a possible error to avoid deadlock situations.
defer func() {
- e := unlockFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, unlockFunc())
}()
if err != nil {
return
diff --git a/utils/coreutils/tableutils.go b/utils/coreutils/tableutils.go
index afe507016..cfa3a3eb1 100644
--- a/utils/coreutils/tableutils.go
+++ b/utils/coreutils/tableutils.go
@@ -2,6 +2,7 @@ package coreutils
import (
"bufio"
+ "errors"
"fmt"
"math"
"os"
@@ -136,10 +137,7 @@ func PrintTable(rows interface{}, title string, emptyTableMessage string, printE
tableWriter.Style().Options.SeparateRows = true
stdoutWriter := bufio.NewWriter(os.Stdout)
defer func() {
- e := stdoutWriter.Flush()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, stdoutWriter.Flush())
}()
tableWriter.SetOutputMirror(stdoutWriter)
tableWriter.Render()
diff --git a/utils/coreutils/techutils.go b/utils/coreutils/techutils.go
index 0d51b209d..5a24ef707 100644
--- a/utils/coreutils/techutils.go
+++ b/utils/coreutils/techutils.go
@@ -82,10 +82,11 @@ var technologiesData = map[Technology]TechData{
packageInstallationCommand: "install",
},
Pnpm: {
- indicators: []string{"pnpm-lock.yaml"},
- exclude: []string{".yarnrc.yml", "yarn.lock", ".yarn"},
- packageDescriptors: []string{"package.json"},
- packageVersionOperator: "@",
+ indicators: []string{"pnpm-lock.yaml"},
+ exclude: []string{".yarnrc.yml", "yarn.lock", ".yarn"},
+ packageDescriptors: []string{"package.json"},
+ packageVersionOperator: "@",
+ packageInstallationCommand: "update",
},
Yarn: {
indicators: []string{".yarnrc.yml", "yarn.lock", ".yarn", ".yarnrc"},
diff --git a/utils/dependencies/utils.go b/utils/dependencies/utils.go
index 4f105002c..7be49aa89 100644
--- a/utils/dependencies/utils.go
+++ b/utils/dependencies/utils.go
@@ -39,8 +39,7 @@ func DownloadExtractor(targetPath, downloadPath string) error {
func CreateChecksumFile(targetPath, checksum string) (err error) {
out, err := os.Create(targetPath)
defer func() {
- e := errorutils.CheckError(out.Close())
- err = errors.Join(err, e)
+ err = errors.Join(err, errorutils.CheckError(out.Close()))
}()
if errorutils.CheckError(err) != nil {
return err
diff --git a/utils/java/deptreemanager.go b/utils/java/deptreemanager.go
deleted file mode 100644
index e323fad1e..000000000
--- a/utils/java/deptreemanager.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package java
-
-import (
- "encoding/json"
- "os"
- "strings"
-
- "github.com/jfrog/jfrog-cli-core/v2/utils/config"
- "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
- "github.com/jfrog/jfrog-cli-core/v2/utils/xray"
- "github.com/jfrog/jfrog-client-go/utils/errorutils"
- xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
-)
-
-const (
- GavPackageTypeIdentifier = "gav://"
-)
-
-func BuildDependencyTree(depTreeParams DepTreeParams, tech coreutils.Technology) ([]*xrayUtils.GraphNode, map[string][]string, error) {
- if tech == coreutils.Maven {
- return buildMavenDependencyTree(&depTreeParams)
- }
- return buildGradleDependencyTree(&depTreeParams)
-}
-
-type DepTreeParams struct {
- UseWrapper bool
- Server *config.ServerDetails
- DepsRepo string
- IsMavenDepTreeInstalled bool
- IsCurationCmd bool
- CurationCacheFolder string
-}
-
-type DepTreeManager struct {
- server *config.ServerDetails
- depsRepo string
- useWrapper bool
-}
-
-func NewDepTreeManager(params *DepTreeParams) DepTreeManager {
- return DepTreeManager{useWrapper: params.UseWrapper, depsRepo: params.DepsRepo, server: params.Server}
-}
-
-// The structure of a dependency tree of a module in a Gradle/Maven project, as created by the gradle-dep-tree and maven-dep-tree plugins.
-type moduleDepTree struct {
- Root string `json:"root"`
- Nodes map[string]xray.DepTreeNode `json:"nodes"`
-}
-
-// Reads the output files of the gradle-dep-tree and maven-dep-tree plugins and returns them as a slice of GraphNodes.
-// It takes the output of the plugin's run (which is a byte representation of a list of paths of the output files, separated by newlines) as input.
-func getGraphFromDepTree(outputFilePaths string) (depsGraph []*xrayUtils.GraphNode, uniqueDepsMap map[string][]string, err error) {
- modules, err := parseDepTreeFiles(outputFilePaths)
- if err != nil {
- return
- }
- uniqueDepsMap = map[string][]string{}
- for _, module := range modules {
- moduleTree, moduleUniqueDeps := GetModuleTreeAndDependencies(module)
- depsGraph = append(depsGraph, moduleTree)
- for depToAdd, depTypes := range moduleUniqueDeps {
- uniqueDepsMap[depToAdd] = depTypes
- }
- }
- return
-}
-
-// Returns a dependency tree and a flat list of the module's dependencies for the given module
-func GetModuleTreeAndDependencies(module *moduleDepTree) (*xrayUtils.GraphNode, map[string][]string) {
- moduleTreeMap := make(map[string]xray.DepTreeNode)
- moduleDeps := module.Nodes
- for depName, dependency := range moduleDeps {
- dependencyId := GavPackageTypeIdentifier + depName
- var childrenList []string
- for _, childName := range dependency.Children {
- childId := GavPackageTypeIdentifier + childName
- childrenList = append(childrenList, childId)
- }
- moduleTreeMap[dependencyId] = xray.DepTreeNode{
- Types: dependency.Types,
- Children: childrenList,
- }
- }
- return xray.BuildXrayDependencyTree(moduleTreeMap, GavPackageTypeIdentifier+module.Root)
-}
-
-func parseDepTreeFiles(jsonFilePaths string) ([]*moduleDepTree, error) {
- outputFilePaths := strings.Split(strings.TrimSpace(jsonFilePaths), "\n")
- var modules []*moduleDepTree
- for _, path := range outputFilePaths {
- results, err := parseDepTreeFile(path)
- if err != nil {
- return nil, err
- }
- modules = append(modules, results)
- }
- return modules, nil
-}
-
-func parseDepTreeFile(path string) (results *moduleDepTree, err error) {
- depTreeJson, err := os.ReadFile(strings.TrimSpace(path))
- if errorutils.CheckError(err) != nil {
- return
- }
- results = &moduleDepTree{}
- err = errorutils.CheckError(json.Unmarshal(depTreeJson, &results))
- return
-}
-
-func getArtifactoryAuthFromServer(server *config.ServerDetails) (string, string, error) {
- username, password, err := server.GetAuthenticationCredentials()
- if err != nil {
- return "", "", err
- }
- if username == "" {
- return "", "", errorutils.CheckErrorf("a username is required for authenticating with Artifactory")
- }
- return username, password, nil
-}
-
-func (dtm *DepTreeManager) GetDepsRepo() string {
- return dtm.depsRepo
-}
diff --git a/utils/java/deptreemanager_test.go b/utils/java/deptreemanager_test.go
deleted file mode 100644
index 6eb23b3d8..000000000
--- a/utils/java/deptreemanager_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package java
-
-import (
- "os"
- "path/filepath"
- "reflect"
- "strings"
- "testing"
-
- "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
- "github.com/stretchr/testify/assert"
-)
-
-func TestGetGradleGraphFromDepTree(t *testing.T) {
- // Create and change directory to test workspace
- tempDirPath, cleanUp := tests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "gradle"))
- defer cleanUp()
- assert.NoError(t, os.Chmod(filepath.Join(tempDirPath, "gradlew"), 0700))
- expectedTree := map[string]map[string]string{
- "org.jfrog.example.gradle:shared:1.0": {},
- "org.jfrog.example.gradle:" + filepath.Base(tempDirPath) + ":1.0": {},
- "org.jfrog.example.gradle:services:1.0": {},
- "org.jfrog.example.gradle:webservice:1.0": {
- "junit:junit:4.11": "",
- "commons-io:commons-io:1.2": "",
- "org.apache.wicket:wicket:1.3.7": "",
- "org.jfrog.example.gradle:shared:1.0": "",
- "org.jfrog.example.gradle:api:1.0": "",
- "commons-lang:commons-lang:2.4": "",
- "commons-collections:commons-collections:3.2": "",
- },
- "org.jfrog.example.gradle:api:1.0": {
- "org.apache.wicket:wicket:1.3.7": "",
- "org.jfrog.example.gradle:shared:1.0": "",
- "commons-lang:commons-lang:2.4": "",
- },
- }
- expectedUniqueDeps := []string{
- "junit:junit:4.11",
- "org.jfrog.example.gradle:webservice:1.0",
- "org.jfrog.example.gradle:api:1.0",
- "org.jfrog.example.gradle:" + filepath.Base(tempDirPath) + ":1.0",
- "commons-io:commons-io:1.2",
- "org.apache.wicket:wicket:1.3.7",
- "org.jfrog.example.gradle:shared:1.0",
- "org.jfrog.example.gradle:api:1.0",
- "commons-collections:commons-collections:3.2",
- "commons-lang:commons-lang:2.4",
- "org.hamcrest:hamcrest-core:1.3",
- "org.slf4j:slf4j-api:1.4.2",
- }
-
- manager := &gradleDepTreeManager{DepTreeManager{}}
- outputFileContent, err := manager.runGradleDepTree()
- assert.NoError(t, err)
- depTree, uniqueDeps, err := getGraphFromDepTree(outputFileContent)
- assert.NoError(t, err)
- reflect.DeepEqual(uniqueDeps, expectedUniqueDeps)
-
- for _, dependency := range depTree {
- dependencyId := strings.TrimPrefix(dependency.Id, GavPackageTypeIdentifier)
- depChild, exists := expectedTree[dependencyId]
- assert.True(t, exists)
- assert.Equal(t, len(depChild), len(dependency.Nodes))
- }
-}
diff --git a/utils/java/gradle.go b/utils/java/gradle.go
deleted file mode 100644
index 57ceff8bd..000000000
--- a/utils/java/gradle.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package java
-
-import (
- _ "embed"
- "errors"
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
-
- "github.com/jfrog/build-info-go/build"
- "github.com/jfrog/jfrog-cli-core/v2/utils/config"
- "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
- "github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
- "github.com/jfrog/jfrog-client-go/utils/errorutils"
- "github.com/jfrog/jfrog-client-go/utils/io/fileutils"
- "github.com/jfrog/jfrog-client-go/utils/log"
- xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
-)
-
-const (
- remoteDepTreePath = "artifactory/oss-release-local"
- gradlew = "gradlew"
- gradleDepTreeJarFile = "gradle-dep-tree.jar"
- gradleDepTreeInitFile = "gradledeptree.init"
- gradleDepTreeOutputFile = "gradledeptree.out"
- gradleDepTreeInitScript = `initscript {
- repositories { %s
- mavenCentral()
- }
- dependencies {
- classpath files('%s')
- }
-}
-
-allprojects {
- repositories { %s
- }
- apply plugin: com.jfrog.GradleDepTree
-}`
- artifactoryRepository = `
- maven {
- url "%s/%s"
- credentials {
- username = '%s'
- password = '%s'
- }
- }`
-)
-
-//go:embed resources/gradle-dep-tree.jar
-var gradleDepTreeJar []byte
-
-type gradleDepTreeManager struct {
- DepTreeManager
-}
-
-func buildGradleDependencyTree(params *DepTreeParams) (dependencyTree []*xrayUtils.GraphNode, uniqueDeps map[string][]string, err error) {
- manager := &gradleDepTreeManager{DepTreeManager: NewDepTreeManager(params)}
- outputFileContent, err := manager.runGradleDepTree()
- if err != nil {
- return
- }
- dependencyTree, uniqueDeps, err = getGraphFromDepTree(outputFileContent)
- return
-}
-
-func (gdt *gradleDepTreeManager) runGradleDepTree() (string, error) {
- // Create the script file in the repository
- depTreeDir, err := gdt.createDepTreeScriptAndGetDir()
- if err != nil {
- return "", err
- }
- defer func() {
- err = errors.Join(err, fileutils.RemoveTempDir(depTreeDir))
- }()
-
- if gdt.useWrapper {
- gdt.useWrapper, err = isGradleWrapperExist()
- if err != nil {
- return "", err
- }
- }
-
- output, err := gdt.execGradleDepTree(depTreeDir)
- if err != nil {
- return "", err
- }
- return string(output), nil
-}
-
-func (gdt *gradleDepTreeManager) createDepTreeScriptAndGetDir() (tmpDir string, err error) {
- tmpDir, err = fileutils.CreateTempDir()
- if err != nil {
- return
- }
- var releasesRepo string
- releasesRepo, gdt.depsRepo, err = getRemoteRepos(gdt.depsRepo, gdt.server)
- if err != nil {
- return
- }
- gradleDepTreeJarPath := filepath.Join(tmpDir, gradleDepTreeJarFile)
- if err = errorutils.CheckError(os.WriteFile(gradleDepTreeJarPath, gradleDepTreeJar, 0600)); err != nil {
- return
- }
- gradleDepTreeJarPath = ioutils.DoubleWinPathSeparator(gradleDepTreeJarPath)
-
- depTreeInitScript := fmt.Sprintf(gradleDepTreeInitScript, releasesRepo, gradleDepTreeJarPath, gdt.depsRepo)
- return tmpDir, errorutils.CheckError(os.WriteFile(filepath.Join(tmpDir, gradleDepTreeInitFile), []byte(depTreeInitScript), 0666))
-}
-
-// getRemoteRepos constructs the sections of Artifactory's remote repositories in the gradle-dep-tree init script.
-// depsRemoteRepo - name of the remote repository that proxies the relevant registry, e.g. maven central.
-// server - the Artifactory server details on which the repositories reside in.
-// Returns the constructed sections.
-func getRemoteRepos(depsRepo string, server *config.ServerDetails) (string, string, error) {
- constructedReleasesRepo, err := constructReleasesRemoteRepo()
- if err != nil {
- return "", "", err
- }
-
- constructedDepsRepo, err := getDepTreeArtifactoryRepository(depsRepo, server)
- if err != nil {
- return "", "", err
- }
- return constructedReleasesRepo, constructedDepsRepo, nil
-}
-
-func constructReleasesRemoteRepo() (string, error) {
- // Try to retrieve the serverID and remote repository that proxies https://releases.jfrog.io, from the environment variable
- serverId, repoName, err := coreutils.GetServerIdAndRepo(coreutils.ReleasesRemoteEnv)
- if err != nil || serverId == "" || repoName == "" {
- return "", err
- }
-
- releasesServer, err := config.GetSpecificConfig(serverId, false, true)
- if err != nil {
- return "", err
- }
-
- releasesPath := fmt.Sprintf("%s/%s", repoName, remoteDepTreePath)
- log.Debug("The `"+gradleDepTreeJarFile+"` will be resolved from", repoName)
- return getDepTreeArtifactoryRepository(releasesPath, releasesServer)
-}
-
-func (gdt *gradleDepTreeManager) execGradleDepTree(depTreeDir string) (outputFileContent []byte, err error) {
- gradleExecPath, err := build.GetGradleExecPath(gdt.useWrapper)
- if err != nil {
- err = errorutils.CheckError(err)
- return
- }
-
- outputFilePath := filepath.Join(depTreeDir, gradleDepTreeOutputFile)
- tasks := []string{
- "clean",
- "generateDepTrees", "-I", filepath.Join(depTreeDir, gradleDepTreeInitFile),
- "-q",
- fmt.Sprintf("-Dcom.jfrog.depsTreeOutputFile=%s", outputFilePath),
- "-Dcom.jfrog.includeAllBuildFiles=true"}
- log.Info("Running gradle deps tree command:", gradleExecPath, strings.Join(tasks, " "))
- if output, err := exec.Command(gradleExecPath, tasks...).CombinedOutput(); err != nil {
- return nil, errorutils.CheckErrorf("error running gradle-dep-tree: %s\n%s", err.Error(), string(output))
- }
- defer func() {
- err = errors.Join(err, errorutils.CheckError(os.Remove(outputFilePath)))
- }()
-
- outputFileContent, err = os.ReadFile(outputFilePath)
- err = errorutils.CheckError(err)
- return
-}
-
-func getDepTreeArtifactoryRepository(remoteRepo string, server *config.ServerDetails) (string, error) {
- if remoteRepo == "" || server.IsEmpty() {
- return "", nil
- }
- username, password, err := getArtifactoryAuthFromServer(server)
- if err != nil {
- return "", err
- }
-
- log.Debug("The project dependencies will be resolved from", server.ArtifactoryUrl, "from the", remoteRepo, "repository")
- return fmt.Sprintf(artifactoryRepository,
- strings.TrimSuffix(server.ArtifactoryUrl, "/"),
- remoteRepo,
- username,
- password), nil
-}
-
-// This function assumes that the Gradle wrapper is in the root directory.
-// The --project-dir option of Gradle won't work in this case.
-func isGradleWrapperExist() (bool, error) {
- wrapperName := gradlew
- if coreutils.IsWindows() {
- wrapperName += ".bat"
- }
- return fileutils.IsFileExists(wrapperName, false)
-}
diff --git a/utils/java/gradle_test.go b/utils/java/gradle_test.go
deleted file mode 100644
index 787542fa4..000000000
--- a/utils/java/gradle_test.go
+++ /dev/null
@@ -1,230 +0,0 @@
-package java
-
-import (
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/jfrog/jfrog-cli-core/v2/utils/config"
- testsutils "github.com/jfrog/jfrog-cli-core/v2/utils/config/tests"
- "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
- "github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
- "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
-
- "github.com/stretchr/testify/assert"
-)
-
-// #nosec G101 -- Dummy token for tests
-// jfrog-ignore
-const dummyToken = "eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJIcnU2VHctZk1yOTV3dy12TDNjV3ZBVjJ3Qm9FSHpHdGlwUEFwOE1JdDljIn0.eyJzdWIiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3XC91c2Vyc1wvYWRtaW4iLCJzY3AiOiJtZW1iZXItb2YtZ3JvdXBzOnJlYWRlcnMgYXBpOioiLCJhdWQiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3IiwiaXNzIjoiamZydEAwMWMzZ2ZmaGcyZTh3NjE0OWUzYTJxMHc5NyIsImV4cCI6MTU1NjAzNzc2NSwiaWF0IjoxNTU2MDM0MTY1LCJqdGkiOiI1M2FlMzgyMy05NGM3LTQ0OGItOGExOC1iZGVhNDBiZjFlMjAifQ.Bp3sdvppvRxysMlLgqT48nRIHXISj9sJUCXrm7pp8evJGZW1S9hFuK1olPmcSybk2HNzdzoMcwhUmdUzAssiQkQvqd_HanRcfFbrHeg5l1fUQ397ECES-r5xK18SYtG1VR7LNTVzhJqkmRd3jzqfmIK2hKWpEgPfm8DRz3j4GGtDRxhb3oaVsT2tSSi_VfT3Ry74tzmO0GcCvmBE2oh58kUZ4QfEsalgZ8IpYHTxovsgDx_M7ujOSZx_hzpz-iy268-OkrU22PQPCfBmlbEKeEUStUO9n0pj4l1ODL31AGARyJRy46w4yzhw7Fk5P336WmDMXYs5LAX2XxPFNLvNzA"
-
-const expectedInitScriptWithRepos = `initscript {
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath files('%s')
- }
-}
-
-allprojects {
- repositories {
- maven {
- url "https://myartifactory.com/artifactory/deps-repo"
- credentials {
- username = 'admin'
- password = '%s'
- }
- }
- }
- apply plugin: com.jfrog.GradleDepTree
-}`
-
-func TestGradleTreesWithoutConfig(t *testing.T) {
- // Create and change directory to test workspace
- tempDirPath, cleanUp := tests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "gradle"))
- defer cleanUp()
- assert.NoError(t, os.Chmod(filepath.Join(tempDirPath, "gradlew"), 0700))
-
- // Run getModulesDependencyTrees
- modulesDependencyTrees, uniqueDeps, err := buildGradleDependencyTree(&DepTreeParams{})
- if assert.NoError(t, err) && assert.NotNil(t, modulesDependencyTrees) {
- assert.Len(t, uniqueDeps, 12)
- assert.Len(t, modulesDependencyTrees, 5)
- // Check module
- module := tests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.example.gradle:webservice:1.0")
- assert.Len(t, module.Nodes, 7)
-
- // Check direct dependency
- directDependency := tests.GetAndAssertNode(t, module.Nodes, "junit:junit:4.11")
- assert.Len(t, directDependency.Nodes, 1)
-
- // Check transitive dependency
- tests.GetAndAssertNode(t, directDependency.Nodes, "org.hamcrest:hamcrest-core:1.3")
- }
-}
-
-func TestGradleTreesWithConfig(t *testing.T) {
- // Create and change directory to test workspace
- tempDirPath, cleanUp := tests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "gradle-example-config"))
- defer cleanUp()
- assert.NoError(t, os.Chmod(filepath.Join(tempDirPath, "gradlew"), 0700))
-
- // Run getModulesDependencyTrees
- modulesDependencyTrees, uniqueDeps, err := buildGradleDependencyTree(&DepTreeParams{UseWrapper: true})
- if assert.NoError(t, err) && assert.NotNil(t, modulesDependencyTrees) {
- assert.Len(t, modulesDependencyTrees, 5)
- assert.Len(t, uniqueDeps, 11)
- // Check module
- module := tests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test.gradle.publish:api:1.0-SNAPSHOT")
- assert.Len(t, module.Nodes, 4)
-
- // Check direct dependency
- directDependency := tests.GetAndAssertNode(t, module.Nodes, "commons-lang:commons-lang:2.4")
- assert.Len(t, directDependency.Nodes, 1)
-
- // Check transitive dependency
- tests.GetAndAssertNode(t, directDependency.Nodes, "commons-io:commons-io:1.2")
- }
-}
-
-func TestIsGradleWrapperExist(t *testing.T) {
- // Check Gradle wrapper doesn't exist
- isWrapperExist, err := isGradleWrapperExist()
- assert.False(t, isWrapperExist)
- assert.NoError(t, err)
-
- // Check Gradle wrapper exist
- _, cleanUp := tests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "gradle"))
- defer cleanUp()
- isWrapperExist, err = isGradleWrapperExist()
- assert.NoError(t, err)
- assert.True(t, isWrapperExist)
-}
-
-func TestGetDepTreeArtifactoryRepository(t *testing.T) {
- tests := []struct {
- name string
- remoteRepo string
- server *config.ServerDetails
- expectedUrl string
- expectedErr string
- }{
- {
- name: "WithAccessToken",
- remoteRepo: "my-remote-repo",
- server: &config.ServerDetails{
- Url: "https://myartifactory.com",
- // jfrog-ignore
- AccessToken: dummyToken,
- },
- // jfrog-ignore
- expectedUrl: "\n\t\tmaven {\n\t\t\turl \"/my-remote-repo\"\n\t\t\tcredentials {\n\t\t\t\tusername = 'admin'\n\t\t\t\tpassword = 'eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJIcnU2VHctZk1yOTV3dy12TDNjV3ZBVjJ3Qm9FSHpHdGlwUEFwOE1JdDljIn0.eyJzdWIiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3XC91c2Vyc1wvYWRtaW4iLCJzY3AiOiJtZW1iZXItb2YtZ3JvdXBzOnJlYWRlcnMgYXBpOioiLCJhdWQiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3IiwiaXNzIjoiamZydEAwMWMzZ2ZmaGcyZTh3NjE0OWUzYTJxMHc5NyIsImV4cCI6MTU1NjAzNzc2NSwiaWF0IjoxNTU2MDM0MTY1LCJqdGkiOiI1M2FlMzgyMy05NGM3LTQ0OGItOGExOC1iZGVhNDBiZjFlMjAifQ.Bp3sdvppvRxysMlLgqT48nRIHXISj9sJUCXrm7pp8evJGZW1S9hFuK1olPmcSybk2HNzdzoMcwhUmdUzAssiQkQvqd_HanRcfFbrHeg5l1fUQ397ECES-r5xK18SYtG1VR7LNTVzhJqkmRd3jzqfmIK2hKWpEgPfm8DRz3j4GGtDRxhb3oaVsT2tSSi_VfT3Ry74tzmO0GcCvmBE2oh58kUZ4QfEsalgZ8IpYHTxovsgDx_M7ujOSZx_hzpz-iy268-OkrU22PQPCfBmlbEKeEUStUO9n0pj4l1ODL31AGARyJRy46w4yzhw7Fk5P336WmDMXYs5LAX2XxPFNLvNzA'\n\t\t\t}\n\t\t}",
- expectedErr: "",
- },
- {
- name: "WithUsernameAndPassword",
- remoteRepo: "my-remote-repo",
- server: &config.ServerDetails{
- Url: "https://myartifactory.com",
- User: "my-username",
- Password: "my-password",
- },
- expectedUrl: "\n\t\tmaven {\n\t\t\turl \"/my-remote-repo\"\n\t\t\tcredentials {\n\t\t\t\tusername = 'my-username'\n\t\t\t\tpassword = 'my-password'\n\t\t\t}\n\t\t}",
- expectedErr: "",
- },
- {
- name: "MissingCredentials",
- remoteRepo: "my-remote-repo",
- server: &config.ServerDetails{
- Url: "https://myartifactory.com",
- },
- expectedUrl: "",
- expectedErr: "either username/password or access token must be set for https://myartifactory.com",
- },
- }
-
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- url, err := getDepTreeArtifactoryRepository(test.remoteRepo, test.server)
- if err != nil {
- assert.Equal(t, test.expectedErr, err.Error())
- } else {
- assert.Equal(t, test.expectedUrl, url)
- }
- })
- }
-}
-
-func TestCreateDepTreeScript(t *testing.T) {
- manager := &gradleDepTreeManager{DepTreeManager: DepTreeManager{}}
- tmpDir, err := manager.createDepTreeScriptAndGetDir()
- assert.NoError(t, err)
- defer func() {
- assert.NoError(t, os.Remove(filepath.Join(tmpDir, gradleDepTreeInitFile)))
- }()
- content, err := os.ReadFile(filepath.Join(tmpDir, gradleDepTreeInitFile))
- assert.NoError(t, err)
- gradleDepTreeJarPath := ioutils.DoubleWinPathSeparator(filepath.Join(tmpDir, gradleDepTreeJarFile))
- assert.Equal(t, fmt.Sprintf(gradleDepTreeInitScript, "", gradleDepTreeJarPath, ""), string(content))
-}
-
-func TestCreateDepTreeScriptWithRepositories(t *testing.T) {
- manager := &gradleDepTreeManager{DepTreeManager: DepTreeManager{}}
- manager.depsRepo = "deps-repo"
- manager.server = &config.ServerDetails{
- Url: "https://myartifactory.com/",
- ArtifactoryUrl: "https://myartifactory.com/artifactory",
- // jfrog-ignore
- AccessToken: dummyToken,
- }
- tmpDir, err := manager.createDepTreeScriptAndGetDir()
- assert.NoError(t, err)
- defer func() {
- assert.NoError(t, os.Remove(filepath.Join(tmpDir, gradleDepTreeInitFile)))
- }()
-
- content, err := os.ReadFile(filepath.Join(tmpDir, gradleDepTreeInitFile))
- assert.NoError(t, err)
- gradleDepTreeJarPath := ioutils.DoubleWinPathSeparator(filepath.Join(tmpDir, gradleDepTreeJarFile))
- // jfrog-ignore
- assert.Equal(t, fmt.Sprintf(expectedInitScriptWithRepos, gradleDepTreeJarPath, dummyToken), string(content))
-}
-
-func TestConstructReleasesRemoteRepo(t *testing.T) {
- cleanUp := testsutils.CreateTempEnv(t, false)
- serverDetails := &config.ServerDetails{
- ServerId: "test",
- ArtifactoryUrl: "https://domain.com/artifactory",
- User: "user",
- Password: "pass",
- }
- err := config.SaveServersConf([]*config.ServerDetails{serverDetails})
- assert.NoError(t, err)
- defer cleanUp()
- testCases := []struct {
- envVar string
- expectedRepo string
- expectedErr error
- }{
- {envVar: "", expectedRepo: "", expectedErr: nil},
- {envVar: "test/repo1", expectedRepo: "\n\t\tmaven {\n\t\t\turl \"https://domain.com/artifactory/repo1/artifactory/oss-release-local\"\n\t\t\tcredentials {\n\t\t\t\tusername = 'user'\n\t\t\t\tpassword = 'pass'\n\t\t\t}\n\t\t}", expectedErr: nil},
- {envVar: "notexist/repo1", expectedRepo: "", expectedErr: errors.New("Server ID 'notexist' does not exist.")},
- }
-
- for _, tc := range testCases {
- // Set the environment variable for this test case
- func() {
- assert.NoError(t, os.Setenv(coreutils.ReleasesRemoteEnv, tc.envVar))
- defer func() {
- // Reset the environment variable after each test case
- assert.NoError(t, os.Unsetenv(coreutils.ReleasesRemoteEnv))
- }()
- actualRepo, actualErr := constructReleasesRemoteRepo()
- assert.Equal(t, tc.expectedRepo, actualRepo)
- assert.Equal(t, tc.expectedErr, actualErr)
- }()
- }
-}
diff --git a/utils/java/mvn.go b/utils/java/mvn.go
deleted file mode 100644
index 76514fb11..000000000
--- a/utils/java/mvn.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package java
-
-import (
- _ "embed"
- "errors"
- "fmt"
- "net/url"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "strings"
-
- "github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
- "github.com/jfrog/jfrog-client-go/utils/errorutils"
- "github.com/jfrog/jfrog-client-go/utils/io/fileutils"
- "github.com/jfrog/jfrog-client-go/utils/log"
- xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
-)
-
-const (
- mavenDepTreeJarFile = "maven-dep-tree.jar"
- mavenDepTreeOutputFile = "mavendeptree.out"
- // Changing this version also requires a change in MAVEN_DEP_TREE_VERSION within buildscripts/download_jars.sh
- mavenDepTreeVersion = "1.1.0"
- settingsXmlFile = "settings.xml"
-)
-
-var mavenConfigPath = filepath.Join(".mvn", "maven.config")
-
-type MavenDepTreeCmd string
-
-const (
- Projects MavenDepTreeCmd = "projects"
- Tree MavenDepTreeCmd = "tree"
-)
-
-//go:embed resources/settings.xml
-var settingsXmlTemplate string
-
-//go:embed resources/maven-dep-tree.jar
-var mavenDepTreeJar []byte
-
-type MavenDepTreeManager struct {
- DepTreeManager
- isInstalled bool
- // this flag its curation command, it will set dedicated cache and download url.
- isCurationCmd bool
- // path to the curation dedicated cache
- curationCacheFolder string
- cmdName MavenDepTreeCmd
- settingsXmlPath string
-}
-
-func NewMavenDepTreeManager(params *DepTreeParams, cmdName MavenDepTreeCmd) *MavenDepTreeManager {
- depTreeManager := NewDepTreeManager(&DepTreeParams{
- Server: params.Server,
- DepsRepo: params.DepsRepo,
- })
- return &MavenDepTreeManager{
- DepTreeManager: depTreeManager,
- isInstalled: params.IsMavenDepTreeInstalled,
- cmdName: cmdName,
- isCurationCmd: params.IsCurationCmd,
- curationCacheFolder: params.CurationCacheFolder,
- }
-}
-
-func buildMavenDependencyTree(params *DepTreeParams) (dependencyTree []*xrayUtils.GraphNode, uniqueDeps map[string][]string, err error) {
- manager := NewMavenDepTreeManager(params, Tree)
- outputFilePaths, clearMavenDepTreeRun, err := manager.RunMavenDepTree()
- if err != nil {
- if clearMavenDepTreeRun != nil {
- err = errors.Join(err, clearMavenDepTreeRun())
- }
- return
- }
- defer func() {
- err = errors.Join(err, clearMavenDepTreeRun())
- }()
- dependencyTree, uniqueDeps, err = getGraphFromDepTree(outputFilePaths)
- return
-}
-
-// Runs maven-dep-tree according to cmdName. Returns the plugin output along with a function pointer to revert the plugin side effects.
-// If a non-nil clearMavenDepTreeRun pointer is returnes it means we had no error during the entire function execution
-func (mdt *MavenDepTreeManager) RunMavenDepTree() (depTreeOutput string, clearMavenDepTreeRun func() error, err error) {
- // depTreeExecDir is a temp directory for all the files that are required for the maven-dep-tree run
- depTreeExecDir, clearMavenDepTreeRun, err := mdt.CreateTempDirWithSettingsXmlIfNeeded()
- if err != nil {
- return
- }
- if err = mdt.installMavenDepTreePlugin(depTreeExecDir); err != nil {
- return
- }
-
- depTreeOutput, err = mdt.execMavenDepTree(depTreeExecDir)
- if err != nil {
- return
- }
- return
-}
-
-func (mdt *MavenDepTreeManager) installMavenDepTreePlugin(depTreeExecDir string) error {
- if mdt.isInstalled {
- return nil
- }
- mavenDepTreeJarPath := filepath.Join(depTreeExecDir, mavenDepTreeJarFile)
- if err := errorutils.CheckError(os.WriteFile(mavenDepTreeJarPath, mavenDepTreeJar, 0666)); err != nil {
- return err
- }
- goals := GetMavenPluginInstallationGoals(mavenDepTreeJarPath)
- _, err := mdt.RunMvnCmd(goals)
- return err
-}
-
-func GetMavenPluginInstallationGoals(pluginPath string) []string {
- return []string{"org.apache.maven.plugins:maven-install-plugin:3.1.1:install-file", "-Dfile=" + pluginPath, "-B"}
-}
-
-func (mdt *MavenDepTreeManager) execMavenDepTree(depTreeExecDir string) (string, error) {
- if mdt.cmdName == Tree {
- return mdt.runTreeCmd(depTreeExecDir)
- }
- return mdt.runProjectsCmd()
-}
-
-func (mdt *MavenDepTreeManager) runTreeCmd(depTreeExecDir string) (string, error) {
- mavenDepTreePath := filepath.Join(depTreeExecDir, mavenDepTreeOutputFile)
- goals := []string{"com.jfrog:maven-dep-tree:" + mavenDepTreeVersion + ":" + string(Tree), "-DdepsTreeOutputFile=" + mavenDepTreePath, "-B"}
- if mdt.isCurationCmd {
- goals = append(goals, "-Dmaven.repo.local="+mdt.curationCacheFolder)
- }
- if _, err := mdt.RunMvnCmd(goals); err != nil {
- return "", err
- }
-
- mavenDepTreeOutput, err := os.ReadFile(mavenDepTreePath)
- if err != nil {
- return "", errorutils.CheckError(err)
- }
- return string(mavenDepTreeOutput), nil
-}
-
-func (mdt *MavenDepTreeManager) runProjectsCmd() (string, error) {
- goals := []string{"com.jfrog:maven-dep-tree:" + mavenDepTreeVersion + ":" + string(Projects), "-q"}
- output, err := mdt.RunMvnCmd(goals)
- if err != nil {
- return "", err
- }
- return string(output), nil
-}
-
-func (mdt *MavenDepTreeManager) RunMvnCmd(goals []string) (cmdOutput []byte, err error) {
- restoreMavenConfig, err := removeMavenConfig()
- if err != nil {
- return
- }
-
- defer func() {
- if restoreMavenConfig != nil {
- err = errors.Join(err, restoreMavenConfig())
- }
- }()
-
- if mdt.settingsXmlPath != "" {
- goals = append(goals, "-s", mdt.settingsXmlPath)
- }
-
- //#nosec G204
- cmdOutput, err = exec.Command("mvn", goals...).CombinedOutput()
- if err != nil {
- stringOutput := string(cmdOutput)
- if len(cmdOutput) > 0 {
- log.Info(stringOutput)
- }
- if msg := mdt.suspectCurationBlockedError(stringOutput); msg != "" {
- err = fmt.Errorf("failed running command 'mvn %s\n\n%s", strings.Join(goals, " "), msg)
- } else {
- err = fmt.Errorf("failed running command 'mvn %s': %s", strings.Join(goals, " "), err.Error())
- }
- }
- return
-}
-
-func (mdt *MavenDepTreeManager) GetSettingsXmlPath() string {
- return mdt.settingsXmlPath
-}
-
-func (mdt *MavenDepTreeManager) SetSettingsXmlPath(settingsXmlPath string) {
- mdt.settingsXmlPath = settingsXmlPath
-}
-
-func removeMavenConfig() (func() error, error) {
- mavenConfigExists, err := fileutils.IsFileExists(mavenConfigPath, false)
- if err != nil {
- return nil, err
- }
- if !mavenConfigExists {
- return nil, nil
- }
- restoreMavenConfig, err := ioutils.BackupFile(mavenConfigPath, "maven.config.bkp")
- if err != nil {
- return nil, err
- }
- err = os.Remove(mavenConfigPath)
- if err != nil {
- err = errorutils.CheckErrorf("failed to remove %s while building the maven dependencies tree. Error received:\n%s", mavenConfigPath, err.Error())
- }
- return restoreMavenConfig, err
-}
-
-// Creates a new settings.xml file configured with the provided server and repository from the current MavenDepTreeManager instance.
-// The settings.xml will be written to the given path.
-func (mdt *MavenDepTreeManager) createSettingsXmlWithConfiguredArtifactory(settingsXmlPath string) error {
- username, password, err := getArtifactoryAuthFromServer(mdt.server)
- if err != nil {
- return err
- }
- endPoint := mdt.depsRepo
- if mdt.isCurationCmd {
- endPoint = path.Join("api/curation/audit", endPoint)
- }
- remoteRepositoryFullPath, err := url.JoinPath(mdt.server.ArtifactoryUrl, endPoint)
- if err != nil {
- return err
- }
- mdt.settingsXmlPath = filepath.Join(settingsXmlPath, settingsXmlFile)
- settingsXmlContent := fmt.Sprintf(settingsXmlTemplate, username, password, remoteRepositoryFullPath)
-
- return errorutils.CheckError(os.WriteFile(mdt.settingsXmlPath, []byte(settingsXmlContent), 0600))
-}
-
-// Creates a temporary directory.
-// If Artifactory resolution repo is provided, a settings.xml file with the provided server and repository will be created inside the temprary directory.
-func (mdt *MavenDepTreeManager) CreateTempDirWithSettingsXmlIfNeeded() (tempDirPath string, clearMavenDepTreeRun func() error, err error) {
- tempDirPath, err = fileutils.CreateTempDir()
- if err != nil {
- return
- }
-
- clearMavenDepTreeRun = func() error { return fileutils.RemoveTempDir(tempDirPath) }
-
- // Create a settings.xml file that sets the dependency resolution from the given server and repository
- if mdt.depsRepo != "" {
- err = mdt.createSettingsXmlWithConfiguredArtifactory(tempDirPath)
- }
- if err != nil {
- err = errors.Join(err, clearMavenDepTreeRun())
- clearMavenDepTreeRun = nil
- }
- return
-}
-
-// In case mvn tree fails on 403 or 500 it can be related to packages blocked by curation.
-// For this use case to succeed, pass through should be enabled in the curated repos
-func (mdt *MavenDepTreeManager) suspectCurationBlockedError(cmdOutput string) (msgToUser string) {
- if !mdt.isCurationCmd {
- return
- }
- if strings.Contains(cmdOutput, "status code: 403") || strings.Contains(cmdOutput, "status code: 500") {
- msgToUser = "Failed to get dependencies tree for maven project, Please verify pass-through enabled on the curated repos"
- }
- return msgToUser
-}
diff --git a/utils/java/mvn_test.go b/utils/java/mvn_test.go
deleted file mode 100644
index a527b768b..000000000
--- a/utils/java/mvn_test.go
+++ /dev/null
@@ -1,380 +0,0 @@
-package java
-
-import (
- "github.com/jfrog/build-info-go/utils"
- "github.com/jfrog/jfrog-cli-core/v2/utils/config"
- coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
- "github.com/jfrog/jfrog-client-go/utils/io/fileutils"
- "github.com/jfrog/jfrog-client-go/utils/tests"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "golang.org/x/exp/maps"
- "os"
- "path/filepath"
- "strings"
- "testing"
-)
-
-const (
- //#nosec G101 - dummy token for testing
- settingsXmlWithUsernameAndPassword = `
-
-
-
- artifactory
- testUser
- testPass
-
-
-
-
- artifactory
- https://myartifactory.com/artifactory/testRepo
- *
-
-
-`
- //#nosec G101 - dummy token for testing
- settingsXmlWithUsernameAndPasswordAndCurationDedicatedAPi = `
-
-
-
- artifactory
- testUser
- testPass
-
-
-
-
- artifactory
- https://myartifactory.com/artifactory/api/curation/audit/testRepo
- *
-
-
-`
- //#nosec G101 - dummy token for testing
- settingsXmlWithUsernameAndToken = `
-
-
-
- artifactory
- testUser
- eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJIcnU2VHctZk1yOTV3dy12TDNjV3ZBVjJ3Qm9FSHpHdGlwUEFwOE1JdDljIn0.eyJzdWIiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3XC91c2Vyc1wvYWRtaW4iLCJzY3AiOiJtZW1iZXItb2YtZ3JvdXBzOnJlYWRlcnMgYXBpOioiLCJhdWQiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3IiwiaXNzIjoiamZydEAwMWMzZ2ZmaGcyZTh3NjE0OWUzYTJxMHc5NyIsImV4cCI6MTU1NjAzNzc2NSwiaWF0IjoxNTU2MDM0MTY1LCJqdGkiOiI1M2FlMzgyMy05NGM3LTQ0OGItOGExOC1iZGVhNDBiZjFlMjAifQ.Bp3sdvppvRxysMlLgqT48nRIHXISj9sJUCXrm7pp8evJGZW1S9hFuK1olPmcSybk2HNzdzoMcwhUmdUzAssiQkQvqd_HanRcfFbrHeg5l1fUQ397ECES-r5xK18SYtG1VR7LNTVzhJqkmRd3jzqfmIK2hKWpEgPfm8DRz3j4GGtDRxhb3oaVsT2tSSi_VfT3Ry74tzmO0GcCvmBE2oh58kUZ4QfEsalgZ8IpYHTxovsgDx_M7ujOSZx_hzpz-iy268-OkrU22PQPCfBmlbEKeEUStUO9n0pj4l1ODL31AGARyJRy46w4yzhw7Fk5P336WmDMXYs5LAX2XxPFNLvNzA
-
-
-
-
- artifactory
- https://myartifactory.com/artifactory/testRepo
- *
-
-
-`
- //#nosec G101 - dummy token for testing
- settingsXmlWithAccessToken = `
-
-
-
- artifactory
- admin
- eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJIcnU2VHctZk1yOTV3dy12TDNjV3ZBVjJ3Qm9FSHpHdGlwUEFwOE1JdDljIn0.eyJzdWIiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3XC91c2Vyc1wvYWRtaW4iLCJzY3AiOiJtZW1iZXItb2YtZ3JvdXBzOnJlYWRlcnMgYXBpOioiLCJhdWQiOiJqZnJ0QDAxYzNnZmZoZzJlOHc2MTQ5ZTNhMnEwdzk3IiwiaXNzIjoiamZydEAwMWMzZ2ZmaGcyZTh3NjE0OWUzYTJxMHc5NyIsImV4cCI6MTU1NjAzNzc2NSwiaWF0IjoxNTU2MDM0MTY1LCJqdGkiOiI1M2FlMzgyMy05NGM3LTQ0OGItOGExOC1iZGVhNDBiZjFlMjAifQ.Bp3sdvppvRxysMlLgqT48nRIHXISj9sJUCXrm7pp8evJGZW1S9hFuK1olPmcSybk2HNzdzoMcwhUmdUzAssiQkQvqd_HanRcfFbrHeg5l1fUQ397ECES-r5xK18SYtG1VR7LNTVzhJqkmRd3jzqfmIK2hKWpEgPfm8DRz3j4GGtDRxhb3oaVsT2tSSi_VfT3Ry74tzmO0GcCvmBE2oh58kUZ4QfEsalgZ8IpYHTxovsgDx_M7ujOSZx_hzpz-iy268-OkrU22PQPCfBmlbEKeEUStUO9n0pj4l1ODL31AGARyJRy46w4yzhw7Fk5P336WmDMXYs5LAX2XxPFNLvNzA
-
-
-
-
- artifactory
- https://myartifactory.com/artifactory/testRepo
- *
-
-
-`
-)
-
-func TestMavenTreesMultiModule(t *testing.T) {
- // Create and change directory to test workspace
- _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example"))
- defer cleanUp()
-
- expectedUniqueDeps := []string{
- GavPackageTypeIdentifier + "javax.mail:mail:1.4",
- GavPackageTypeIdentifier + "org.testng:testng:5.9",
- GavPackageTypeIdentifier + "javax.servlet:servlet-api:2.5",
- GavPackageTypeIdentifier + "org.jfrog.test:multi:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "org.jfrog.test:multi3:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "org.jfrog.test:multi2:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "junit:junit:3.8.1",
- GavPackageTypeIdentifier + "org.jfrog.test:multi1:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "commons-io:commons-io:1.4",
- GavPackageTypeIdentifier + "org.apache.commons:commons-email:1.1",
- GavPackageTypeIdentifier + "javax.activation:activation:1.1",
- GavPackageTypeIdentifier + "hsqldb:hsqldb:1.8.0.10",
- }
- // Run getModulesDependencyTrees
- modulesDependencyTrees, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{})
- if assert.NoError(t, err) && assert.NotEmpty(t, modulesDependencyTrees) {
- assert.ElementsMatch(t, maps.Keys(uniqueDeps), expectedUniqueDeps, "First is actual, Second is Expected")
- // Check root module
- multi := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi:3.7-SNAPSHOT")
- if assert.NotNil(t, multi) {
- assert.Len(t, multi.Nodes, 1)
- // Check multi1 with a transitive dependency
- multi1 := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi1:3.7-SNAPSHOT")
- assert.Len(t, multi1.Nodes, 4)
- commonsEmail := coreTests.GetAndAssertNode(t, multi1.Nodes, "org.apache.commons:commons-email:1.1")
- assert.Len(t, commonsEmail.Nodes, 2)
-
- // Check multi2 and multi3
- multi2 := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi2:3.7-SNAPSHOT")
- assert.Len(t, multi2.Nodes, 1)
- multi3 := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi3:3.7-SNAPSHOT")
- assert.Len(t, multi3.Nodes, 4)
- }
- }
-}
-
-func TestMavenWrapperTrees(t *testing.T) {
- // Create and change directory to test workspace
- _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example-with-wrapper"))
- err := os.Chmod("mvnw", 0700)
- defer cleanUp()
- assert.NoError(t, err)
- expectedUniqueDeps := []string{
- GavPackageTypeIdentifier + "org.jfrog.test:multi1:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "org.codehaus.plexus:plexus-utils:1.5.1",
- GavPackageTypeIdentifier + "org.springframework:spring-beans:2.5.6",
- GavPackageTypeIdentifier + "commons-logging:commons-logging:1.1.1",
- GavPackageTypeIdentifier + "org.jfrog.test:multi3:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "org.apache.commons:commons-email:1.1",
- GavPackageTypeIdentifier + "org.springframework:spring-aop:2.5.6",
- GavPackageTypeIdentifier + "org.springframework:spring-core:2.5.6",
- GavPackageTypeIdentifier + "org.jfrog.test:multi:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "org.jfrog.test:multi2:3.7-SNAPSHOT",
- GavPackageTypeIdentifier + "org.testng:testng:5.9",
- GavPackageTypeIdentifier + "hsqldb:hsqldb:1.8.0.10",
- GavPackageTypeIdentifier + "junit:junit:3.8.1",
- GavPackageTypeIdentifier + "javax.activation:activation:1.1",
- GavPackageTypeIdentifier + "javax.mail:mail:1.4",
- GavPackageTypeIdentifier + "aopalliance:aopalliance:1.0",
- GavPackageTypeIdentifier + "commons-io:commons-io:1.4",
- GavPackageTypeIdentifier + "javax.servlet.jsp:jsp-api:2.1",
- GavPackageTypeIdentifier + "javax.servlet:servlet-api:2.5",
- }
-
- modulesDependencyTrees, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{})
- if assert.NoError(t, err) && assert.NotEmpty(t, modulesDependencyTrees) {
- assert.ElementsMatch(t, maps.Keys(uniqueDeps), expectedUniqueDeps, "First is actual, Second is Expected")
- // Check root module
- multi := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi:3.7-SNAPSHOT")
- if assert.NotNil(t, multi) {
- assert.Len(t, multi.Nodes, 1)
- // Check multi1 with a transitive dependency
- multi1 := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi1:3.7-SNAPSHOT")
- assert.Len(t, multi1.Nodes, 7)
- commonsEmail := coreTests.GetAndAssertNode(t, multi1.Nodes, "org.apache.commons:commons-email:1.1")
- assert.Len(t, commonsEmail.Nodes, 2)
- // Check multi2 and multi3
- multi2 := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi2:3.7-SNAPSHOT")
- assert.Len(t, multi2.Nodes, 1)
- multi3 := coreTests.GetAndAssertNode(t, modulesDependencyTrees, "org.jfrog.test:multi3:3.7-SNAPSHOT")
- assert.Len(t, multi3.Nodes, 4)
- }
- }
-}
-
-func TestMavenWrapperTreesTypes(t *testing.T) {
- // Create and change directory to test workspace
- _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example-with-many-types"))
- defer cleanUp()
- tree, uniqueDeps, err := buildMavenDependencyTree(&DepTreeParams{})
- require.NoError(t, err)
- // dependency of pom type
- depWithPomType := uniqueDeps["gav://org.webjars:lodash:4.17.21"]
- assert.NotEmpty(t, depWithPomType)
- assert.Equal(t, depWithPomType[0], "pom")
- existInTreePom := false
- for _, node := range tree[0].Nodes {
- if node.Id == "gav://org.webjars:lodash:4.17.21" {
- nodeTypes := *node.Types
- assert.Equal(t, nodeTypes[0], "pom")
- existInTreePom = true
- }
- }
- assert.True(t, existInTreePom)
-
- // dependency of jar type
- depWithJarType := uniqueDeps["gav://junit:junit:4.11"]
- assert.NotEmpty(t, depWithJarType)
- assert.Equal(t, depWithJarType[0], "jar")
- existInTreeJar := false
- for _, node := range tree[0].Nodes {
- if node.Id == "gav://junit:junit:4.11" {
- nodeTypes := *node.Types
- assert.Equal(t, nodeTypes[0], "jar")
- existInTreeJar = true
- }
- }
- assert.True(t, existInTreeJar)
-}
-
-func TestDepTreeWithDedicatedCache(t *testing.T) {
- // Create and change directory to test workspace
- _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example-with-wrapper"))
- err := os.Chmod("mvnw", 0700)
- defer cleanUp()
- assert.NoError(t, err)
- tempDir := t.TempDir()
- defer assert.NoError(t, utils.RemoveTempDir(tempDir))
- manager := NewMavenDepTreeManager(&DepTreeParams{IsCurationCmd: true, CurationCacheFolder: tempDir}, Tree)
- _, err = manager.runTreeCmd(tempDir)
- require.NoError(t, err)
- // validate one of the jars exist in the dedicated cache for curation
- fileExist, err := utils.IsFileExists(filepath.Join(tempDir, "org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar"), false)
- require.NoError(t, err)
- assert.True(t, fileExist)
-}
-
-func TestGetMavenPluginInstallationArgs(t *testing.T) {
- args := GetMavenPluginInstallationGoals("testPlugin")
- assert.Equal(t, "org.apache.maven.plugins:maven-install-plugin:3.1.1:install-file", args[0])
- assert.Equal(t, "-Dfile=testPlugin", args[1])
-}
-
-func TestCreateSettingsXmlWithConfiguredArtifactory(t *testing.T) {
- // Test case for successful creation of settings.xml.
- mdt := MavenDepTreeManager{
- DepTreeManager: DepTreeManager{
- server: &config.ServerDetails{
- ArtifactoryUrl: "https://myartifactory.com/artifactory",
- User: "testUser",
- Password: "testPass",
- },
- depsRepo: "testRepo",
- },
- }
- // Create a temporary directory for testing and settings.xml creation
- tempDir := t.TempDir()
- err := mdt.createSettingsXmlWithConfiguredArtifactory(tempDir)
- assert.NoError(t, err)
-
- // Verify settings.xml file creation with username and password
- settingsXmlPath := filepath.Join(tempDir, "settings.xml")
- actualContent, err := os.ReadFile(settingsXmlPath)
- actualContent = []byte(strings.ReplaceAll(string(actualContent), "\r\n", "\n"))
- assert.NoError(t, err)
- assert.Equal(t, settingsXmlWithUsernameAndPassword, string(actualContent))
-
- // check curation command write a dedicated api for curation.
- mdt.isCurationCmd = true
- err = mdt.createSettingsXmlWithConfiguredArtifactory(tempDir)
- require.NoError(t, err)
- actualContent, err = os.ReadFile(settingsXmlPath)
- actualContent = []byte(strings.ReplaceAll(string(actualContent), "\r\n", "\n"))
- assert.NoError(t, err)
- assert.Equal(t, settingsXmlWithUsernameAndPasswordAndCurationDedicatedAPi, string(actualContent))
- mdt.isCurationCmd = false
-
- mdt.server.Password = ""
- // jfrog-ignore
- mdt.server.AccessToken = dummyToken
- err = mdt.createSettingsXmlWithConfiguredArtifactory(tempDir)
- assert.NoError(t, err)
-
- // Verify settings.xml file creation with username and access token
- actualContent, err = os.ReadFile(settingsXmlPath)
- actualContent = []byte(strings.ReplaceAll(string(actualContent), "\r\n", "\n"))
- assert.NoError(t, err)
- assert.Equal(t, settingsXmlWithUsernameAndToken, string(actualContent))
-
- mdt.server.User = ""
- err = mdt.createSettingsXmlWithConfiguredArtifactory(tempDir)
- assert.NoError(t, err)
-
- // Verify settings.xml file creation with access token only
- actualContent, err = os.ReadFile(settingsXmlPath)
- actualContent = []byte(strings.ReplaceAll(string(actualContent), "\r\n", "\n"))
- assert.NoError(t, err)
- assert.Equal(t, settingsXmlWithAccessToken, string(actualContent))
-}
-
-func TestRunProjectsCmd(t *testing.T) {
- // Create and change directory to test workspace
- _, cleanUp := coreTests.CreateTestWorkspace(t, filepath.Join("..", "..", "tests", "testdata", "maven-example"))
- defer cleanUp()
- mvnDepTreeManager := NewMavenDepTreeManager(&DepTreeParams{}, Projects)
- output, clearMavenDepTreeRun, err := mvnDepTreeManager.RunMavenDepTree()
- assert.NoError(t, err)
- assert.NotNil(t, clearMavenDepTreeRun)
-
- pomPathOccurrences := strings.Count(output, "pomPath")
- assert.Equal(t, 4, pomPathOccurrences)
- assert.NoError(t, clearMavenDepTreeRun())
-}
-
-func TestRemoveMavenConfig(t *testing.T) {
- tmpDir := t.TempDir()
- currentDir, err := os.Getwd()
- assert.NoError(t, err)
- restoreDir := tests.ChangeDirWithCallback(t, currentDir, tmpDir)
- defer restoreDir()
-
- // No maven.config exists
- restoreFunc, err := removeMavenConfig()
- assert.Nil(t, restoreFunc)
- assert.Nil(t, err)
-
- // Create maven.config
- err = fileutils.CreateDirIfNotExist(".mvn")
- assert.NoError(t, err)
- file, err := os.Create(mavenConfigPath)
- assert.NoError(t, err)
- err = file.Close()
- assert.NoError(t, err)
- restoreFunc, err = removeMavenConfig()
- assert.NoError(t, err)
- assert.NoFileExists(t, mavenConfigPath)
- err = restoreFunc()
- assert.NoError(t, err)
- assert.FileExists(t, mavenConfigPath)
-}
-
-func TestMavenDepTreeManager_suspectCurationBlockedError(t *testing.T) {
- errPrefix := "[ERROR] Failed to execute goal on project my-app: Could not resolve dependencies for project com.mycompany.app:my-app:jar:1.0-SNAPSHOT: Failed to " +
- "collect dependencies at junit:junit:jar:3.8.1: Failed to read artifact descriptor for junit:junit:jar:3.8.1: " +
- "The following artifacts could not be resolved: junit:junit:pom:3.8.1 (absent): Could not transfer artifact junit:junit:pom:3.8.1 " +
- "from/to artifactory (http://test:8046/artifactory/api/curation/audit/maven-remote):"
- tests := []struct {
- name string
- wantMsgToUser string
- input string
- }{
- {
- name: "failed on 403",
- wantMsgToUser: "Please verify pass-through enabled on the curated repos",
- input: errPrefix + "status code: 403, reason phrase: Forbidden (403)",
- },
- {
- name: "failed on 500",
- wantMsgToUser: "Please verify pass-through enabled on the curated repos",
- input: errPrefix + " status code: 500, reason phrase: Internal Server Error (500)",
- },
- {
- name: "not 403 or 500",
- wantMsgToUser: "",
- input: errPrefix + " status code: 400, reason phrase: Forbidden (400)",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mdt := &MavenDepTreeManager{}
- assert.Contains(t, tt.wantMsgToUser, mdt.suspectCurationBlockedError(tt.input))
- })
- }
-}
diff --git a/utils/java/resources/gradle-dep-tree.jar b/utils/java/resources/gradle-dep-tree.jar
deleted file mode 100644
index b05be1694..000000000
Binary files a/utils/java/resources/gradle-dep-tree.jar and /dev/null differ
diff --git a/utils/java/resources/maven-dep-tree.jar b/utils/java/resources/maven-dep-tree.jar
deleted file mode 100644
index f8d7ff406..000000000
Binary files a/utils/java/resources/maven-dep-tree.jar and /dev/null differ
diff --git a/utils/java/resources/settings.xml b/utils/java/resources/settings.xml
deleted file mode 100644
index 9dce691f8..000000000
--- a/utils/java/resources/settings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
- artifactory
- %s
- %s
-
-
-
-
- artifactory
- %s
- *
-
-
-
\ No newline at end of file
diff --git a/utils/plugins/utils.go b/utils/plugins/utils.go
index c436cb0e0..e678e17c5 100644
--- a/utils/plugins/utils.go
+++ b/utils/plugins/utils.go
@@ -2,6 +2,7 @@ package plugins
import (
"encoding/json"
+ "errors"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-core/v2/utils/lock"
cliLog "github.com/jfrog/jfrog-cli-core/v2/utils/log"
@@ -61,10 +62,7 @@ func readPluginsConfigAndConvertV0tToV1IfNeeded() (err error) {
unlockFunc, err = lock.CreateLock(lockDirPath)
// Defer the lockFile.Unlock() function before throwing a possible error to avoid deadlock situations.
defer func() {
- e := unlockFunc()
- if err == nil {
- err = e
- }
+ err = errors.Join(err, unlockFunc())
}()
if err != nil {
return