Code Monkey home page Code Monkey logo

jfrog-client-go's Introduction

JFrog Go Client

Scanned by Frogbot

Branch Status
master Build status Static Analysis
dev Build status Static Analysis

Table of Contents

General

jfrog-client-go is a library which provides Go APIs to performs actions on JFrog Artifactory, Xray and Distribution from your Go application. The project is still relatively new, and its APIs may therefore change frequently between releases. The library can be used as a go-module, which should be added to your project's go.mod file. As a reference you may look at JFrog CLI' s go.mod file, which uses this library as a dependency.

Pull Requests

We welcome pull requests from the community.

Guidelines

  • If the existing tests do not already cover your changes, please add tests.
  • Pull requests should be created on the dev branch.
  • Please use gofmt for formatting the code before submitting the pull request.

Tests

To run the tests on the source code, you'll need a running JFrog instance. See the Prerequisites column in the Test Types section below for more information.

Use the following command with the below options to run the tests.

go test -v github.com/jfrog/jfrog-client-go/tests -timeout 0 [test-types] [flags]

If you'd like to run a specific test, add the test function name using the -run flag. For example:

go test -v github.com/jfrog/jfrog-client-go/tests -timeout 0 -run TestGetArtifactoryVersionWithCustomHttpClient -test.artifactory -rt.url=http://127.0.0.1:8081/artifactory -rt.user=admin -rt.password=password

Note: The tests create an Artifactory repository named jfrog-client-tests-repo1. Once the tests are completed, the content of this repository is deleted.

Flags

Test Types

Type Description Prerequisites
-test.artifactory Artifactory tests Artifactory Pro
-test.distribution Distribution tests Artifactory with Distribution
-test.xray Xray tests Artifactory with Xray
-test.xsc Xsc tests Xray with Xsc
-test.pipelines Pipelines tests JFrog Pipelines
-test.access Access tests Artifactory Pro
-test.repositories Repositories tests Artifactory Pro
-test.mpu Multipart upload tests Artifactory Pro with S3 storage

Connection Details

Flag Description
-rt.url [Default: http://localhost:8081/artifactory] Artifactory URL.
-ds.url [Optional] JFrog Distribution URL.
-xr.url [Optional] JFrog Xray URL.
-xsc.url [Optional] JFrog Xsc URL.
-pipe.url [Optional] JFrog Pipelines URL.
-access.url [Optional] JFrog Access URL.
-rt.user [Default: admin] Artifactory username.
-rt.password [Default: password] Artifactory password.
-rt.apikey [Optional] Artifactory API key.
-rt.sshKeyPath [Optional] Ssh key file path. Should be used only if the Artifactory URL format is ssh://[domain]:port
-rt.sshPassphrase [Optional] Ssh key passphrase.
-pipe.accessToken [Optional] Pipelines access token.
-pipe.vcsToken [Optional] Vcs token for Pipelines tests (should have admin permissions).
-pipe.vcsRepo [Optional] Vcs full repo path for Pipelines tests (ex: "domain/myrepo").
-pipe.vcsBranch [Optional] Vcs branch for Pipelines tests (ex: "main").
-access.token [Optional] Access access token.
-ci.runId [Optional] A unique identifier used as a suffix to create repositories in the tests.

General APIs

Setting the Logger

Default logger:

log.SetLogger(log.NewLogger(log.INFO, nil))

You may also log to a file, and/or add log prefixes as shown below:

var file *os.File
// Log flags as described in https://pkg.go.dev/log#pkg-constants.
logFlags := Ldate | Ltime
...
log.SetLogger(log.NewLoggerWithFlags(log.DEBUG, file, logFlags))

Setting the Temp Dir

The default temp dir used is 'os.TempDir()'. Use the following API to set a new temp dir:

fileutils.SetTempDirBase(filepath.Join("my", "temp", "path"))

Artifactory APIs

Creating Artifactory Service Manager

Creating Artifactory Details

rtDetails := auth.NewArtifactoryDetails()
rtDetails.SetUrl("http://localhost:8081/artifactory")
rtDetails.SetSshKeyPath("path/to/.ssh/")
rtDetails.SetApiKey("apikey")
rtDetails.SetUser("user")
rtDetails.SetPassword("password")
rtDetails.SetAccessToken("accesstoken")
// if client certificates are required
rtDetails.SetClientCertPath("path/to/.cer")
rtDetails.SetClientCertKeyPath("path/to/.key")

Creating Artifactory Details with Custom HTTP Client

proxyUrl, err := url.Parse("http://proxyIp:proxyPort")
myCustomClient := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}

rtDetails := auth.NewArtifactoryDetails()
rtDetails.SetUrl("http://localhost:8081/artifactory")
rtDetails.SetSshKeysPath("path/to/.ssh/")
rtDetails.SetApiKey("apikey")
rtDetails.SetUser("user")
rtDetails.SetPassword("password")
rtDetails.SetAccessToken("accesstoken")
serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(rtDetails).
    SetDryRun(false).
    SetHttpClient(myCustomClient).
    Build()

Creating Artifactory Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(rtDetails).
    SetCertificatesPath(certPath).
    SetThreads(threads).
    SetDryRun(false).
    // Add [Context](https://golang.org/pkg/context/)
    SetContext(ctx).
    // Optionally overwrite the default dial timeout, which is set to 30 seconds.
    SetDialTimeout(180 * time.Second).
    // Optionally set the total HTTP request timeout.
    SetOverallRequestTimeout(10 * time.Minute).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(8).
    Build()

Creating New Artifactory Service Manager

rtManager, err := artifactory.New(serviceConfig)

Using Artifactory Services

Uploading Files to Artifactory

Using the UploadFiles() function, we can upload files and get the general statistics of the action (The actual number of successful and failed uploads), and the error value if it occurred.

params := services.NewUploadParams()
params.Pattern = "repo/*/*.zip"
params.Target = "repo/path/"
params.AddVcsProps = false
params.BuildProps = "build.name=buildName;build.number=17;build.timestamp=1600856623553"
params.Recursive = true
params.Regexp = false
params.IncludeDirs = false
params.Flat = true
params.ExplodeArchive = false
params.Archive = "zip"
params.Deb = ""
params.Symlink = false
params.Exclusions = "(.*)a.zip"
// Retries default value: 3
params.Retries = 5
// The maximum number of parts that can be concurrently uploaded per file during a multi-part upload. Set to 0 to disable multi-part upload.
// SplitCount default value: 5
params.SplitCount = 10
// The minimum file size in MiB required to attempt a multi-part upload.
// MinSplitSize default value: 200
params.MinSplitSize = 100
// The upload chunk size in MiB that can be concurrently uploaded during a multi-part upload.
params.ChunkSize = 5
// The min file size in bytes for "checksum deploy".
// "Checksum deploy" is the action of calculating the file checksum locally, before
// the upload, and skipping the actual file transfer if the file already
// exists in Artifactory.
// MinChecksumDeploy default value: 10400
params.MinChecksumDeploy = 15360
// Set to false to disable all checksum calculation, including "checksum deploy".
// ChecksumsCalcEnabled default value: true
params.ChecksumsCalcEnabled = false
// Attach properties to the uploaded files
targetProps := utils.NewProperties()
targetProps.AddProperty("key1", "val1")
params.TargetProps = targetProps
// When using the 'archive' option for upload, we can control the target path inside the uploaded archive using placeholders. This operation determines the TargetPathInArchive value.
TargetPathInArchive := "archive/path/"
// Size limit for files to be uploaded.
SizeLimit= &fspatterns.SizeThreshold{SizeInBytes: 10000, Condition: fspatterns.LessThan}
totalUploaded, totalFailed, err := rtManager.UploadFiles(params)

Downloading Files from Artifactory

Using the DownloadFiles() function, we can download files and get the general statistics of the action (The actual number of files downloaded, and the number of files we expected to download). In addition, we get the error value if it occurred.

params := services.NewDownloadParams()
params.Pattern = "repo/*/*.zip"
params.Target = "target/path/"
// Filter the downloaded files by properties.
params.Props = "key1=val1;key2=val2"
params.Recursive = true
params.IncludeDirs = false
params.Flat = false
params.Explode = false
params.Symlink = true
params.ValidateSymlink = false
params.Exclusions = "(.*)a.zip"
// Retries default value: 3
params.Retries = 5
// SplitCount default value: 3
params.SplitCount = 2
// MinSplitSize default value: 5120
params.MinSplitSize = 7168
// Optional fields to avoid AQL request
Sha256 = "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"
Size = 1000
totalDownloaded, totalFailed, err := rtManager.DownloadFiles(params)

Downloading Release Bundles v1 from Artifactory

Using the DownloadFiles() function, we can download release bundles v1 and get the general statistics of the action (The actual number of files downloaded, and the number of files we expected to download). In addition, we get the error value if it occurred.

It is possible to validate the downloaded release bundle's files by providing a local path to a GPG public key file (the public GPG key should of course correspond to the private GPG key which was used to sign the release bundle).

params := services.NewDownloadParams()
// Path on the local file system to which the files should be downloaded.
params.Target = "target/path/"
// Bundle's name and version should be separated with "/".
params.Bundle = "bundleName/10"
// Optional GPG validation
params.PublicGpgKey = "public/key/file/path"
totalDownloaded, totalFailed, err := rtManager.DownloadFiles(params)

Read more about GPG signing release bundles v1 here.

Uploading and Downloading Files with Summary

The methods UploadFilesWithSummary() and DownloadFilesWithSummary() are similar to UploadFlies() and DownloadFlies(), but return an OperationSummary struct, which allows iterating over the details of the uploaded/downloaded files.
The OperationSummary struct contains:

  • TotalSucceeded - the number of successful uploads/downloads
  • TotalFailed - the number of failed uploads/downloads
  • TransferDetailsReader - a ContentReader of FileTransferDetails structs, with a struct for each successful transfer of file
  • ArtifactsDetailsReader - a ContentReader of ArtifactDetails structs, with a struct for each artifact in Artifactory that was uploaded/downloaded successfully

The ContentReaders can be closed separately by calling Close() on each of them, or they both can be closed at once by calling Close() on the OperationSummary struct.

params := services.NewUploadParams()
params.Pattern = "repo/*/*.zip"
params.Target = "repo/path/"

summary, err := rtManager.UploadFilesWithSummary(params)
defer summary.Close()
reader, totalDownloaded, totalExpected, err := rtManager.DownloadFilesWithResultReader(params)

// Iterate over each file
for currentFileInfo := new(utils.FileInfo); reader.NextRecord(currentFileInfo) == nil; currentFileInfo = new(utils.FileInfo) {
     fmt.Printf("File path: %s\n", currentFileInfo.LocalPath)
}

if err := reader.GetError(); err != nil {
    return err
}

Read more about ContentReader.

Copying Files in Artifactory

params := services.NewMoveCopyParams()
params.Pattern = "repo/*/*.zip"
params.Target = "target/path/"
// Filter the files by properties.
params.Props = "key1=val1;key2=val2"
params.Recursive = true
params.Flat = false

rtManager.Copy(params)

Moving Files in Artifactory

params := services.NewMoveCopyParams()
params.Pattern = "repo/*/*.zip"
params.Target = "target/path/"
// Filter the files by properties.
params.Props = "key1=val1;key2=val2"
params.Recursive = true
params.Flat = false

rtManager.Move(params)

Deleting Files from Artifactory

params := services.NewDeleteParams()
params.Pattern = "repo/*/*.zip"
// Filter the files by properties.
params.Props = "key1=val1;key2=val2"
params.Recursive = true

pathsToDelete, err := rtManager.GetPathsToDelete(params)
if err != nil {
    return err
}
defer pathsToDelete.Close()
rtManager.DeleteFiles(pathsToDelete)

Read more about ContentReader.

Searching Files in Artifactory

params := services.NewSearchParams()
params.Pattern = "repo/*/*.zip"
// Filter the files by properties.
params.Props = "key1=val1;key2=val2"
params.Recursive = true

reader, err := rtManager.SearchFiles(params)
if err != nil {
    return err
}
defer reader.Close()

Read more about ContentReader.

Setting Properties on Files in Artifactory

searchParams = services.NewSearchParams()
searchParams.Recursive = true
searchParams.IncludeDirs = false

reader, err = rtManager.SearchFiles(searchParams)
if err != nil {
    return err
}
defer reader.Close()
propsParams = services.NewPropsParams()
propsParams.Pattern = "repo/*/*.zip"
propsParams.Reader = reader
// Filter the files by properties.
propsParams.Props = "key=value"

rtManager.SetProps(propsParams)

Read more about ContentReader.

Deleting Properties from Files in Artifactory

searchParams = services.NewSearchParams()
searchParams.Recursive = true
searchParams.IncludeDirs = false

resultItems, err = rtManager.SearchFiles(searchParams)
if err != nil {
    return err
}
defer reader.Close()
propsParams = services.NewPropsParams()
propsParams.Pattern = "repo/*/*.zip"
propsParams.Reader = reader
// Filter the files by properties.
propsParams.Props = "key=value"

rtManager.DeleteProps(propsParams)

Getting Properties from Files in Artifactory

rtManager.GetItemProperties("repo/path/file")

Read more about ContentReader.

Publishing Build Info to Artifactory

buildInfo := &buildinfo.BuildInfo{}
// Optional Artifactory project key
projectKey := "my-project-key"
...
rtManager.PublishBuildInfo(buildInfo, projectKey)

Fetching Build Info from Artifactory

buildInfoParams := services.NewBuildInfoParams{}
buildInfoParams.BuildName = "buildName"
buildInfoParams.BuildNumber = "LATEST"
// Optional Artifactory project key
buildInfoParams.ProjectKey = "my-project-key"

rtManager.GetBuildInfo(buildInfoParams)

Promoting Published Builds in Artifactory

params := services.NewPromotionParams()
params.BuildName = "buildName"
params.BuildNumber = "10"
params.TargetRepo = "target-repo"
params.Status = "status"
params.Comment = "comment"
params.Copy = &trueValue
params.FailFast = true
params.IncludeDependencies = &falseValue
params.SourceRepo = "source-repo"
// Optional Artifactory project key
params.ProjectKey = "my-project-key"

rtManager.PromoteBuild(params)

Promoting a Docker Image in Artifactory

sourceDockerImage := "hello-world"
sourceRepo := "docker-local-1"
targetRepo := "docker-local-2"
params := services.NewDockerPromoteParams(sourceDockerImage, sourceRepo, targetRepo)

// Optional parameters:
params.TargetDockerImage = "target-docker-image"
params.SourceTag = "42"
params.TargetTag = "43"
params.Copy = true

rtManager.PromoteDocker(params)

Triggering Build Scanning with JFrog Xray

params := services.NewXrayScanParams()
params.BuildName = buildName
params.BuildNumber = buildNumber

rtManager.XrayScanBuild(params)

Discarding Old Builds

params := services.NewDiscardBuildsParams()
params.BuildName = "buildName"
params.MaxDays = "max-days"
params.MaxBuilds = "max-builds"
params.ExcludeBuilds = "1,2"
params.DeleteArtifacts = false
params.Async = false
// Optional Artifactory project key
projectKey := "my-project-key"

rtManager.DiscardBuilds(params)

Cleaning Unreferenced Git LFS Files from Artifactory

params := services.NewGitLfsCleanParams()
params.Refs = "refs/remotes/*"
params.Repo = "my-project-lfs"
params.GitPath = "path/to/git"

reader,err := rtManager.GetUnreferencedGitLfsFiles(params)

defer reader.Close()
rtManager.DeleteFiles(reader)

Executing AQLs

rtManager.Aql(aql string)

Reading Files in Artifactory

rtManager.ReadRemoteFile(FilePath string)

Creating an Artifactory Access Token

params := services.NewCreateTokenParams()
params.Scope = "api:* member-of-groups:readers"
params.Username = "user"
params.ExpiresIn = 3600 // default -1 (use server default)
params.GrantType = "client_credentials"
params.Refreshable = true
params.Audience = "jfrt@<serviceID1> jfrt@<serviceID2>"

results, err := rtManager.CreateToken(params)

Fetching Artifactory Access Tokens

results, err := rtManager.GetTokens()

Fetching Artifactory Access Tokens of a User

results, err := rtManager.GetUserTokens(username)

Refreshing an Artifactory Access Token

params := services.NewRefreshTokenParams()
params.AccessToken = "<access token>"
params.RefreshToken = "<refresh token>"
params.Token.Scope = "api:*"
params.Token.ExpiresIn = 3600
results, err := rtManager.RefreshToken(params)

Revoking an Artifactory Access Token

params := services.NewRevokeTokenParams()

// Provide either TokenId or Token
params.TokenId = "<token id>"
// params.Token = "access token"

err := rtManager.RevokeToken(params)

Create API Key

// Returns an error if API key already exists - use RegenerateAPIKey instead.
apiKey, err := rtManager.CreateAPIKey()

Regenerate API Key

apiKey, err := rtManager.RegenerateAPIKey()

Get API Key

apiKey, err := rtManager.GetAPIKey()

Creating and Updating Local Repository

You can create and update a local repository for the following package types:

Alpine, Bower, Cran, Cargo, Chef, Cocoapods, Composer, Conan, Conda, Debian, Docker, Gems, Generic, Gitlfs, Go, Gradle, Helm, Ivy, Maven, Npm, Nuget, Opkg, Puppet, Pypi, Rpm, Sbt, Swift, Terraform, Vagrant, and Yum.

Each package type has its own parameters struct, can be created using the method New<packageType>LocalRepositoryParams().

Example for creating local Generic repository:

params := services.NewGenericLocalRepositoryParams()
params.Key = "generic-repo"
params.Description = "This is a public description for generic-repo"
params.Notes = "These are internal notes for generic-repo"
params.RepoLayoutRef = "simple-default"
params.ArchiveBrowsingEnabled = true
params.XrayIndex = true
params.IncludesPattern = "**/*"
params.ExcludesPattern = "excludedDir/*"
params.DownloadRedirect = true

err = servicesManager.CreateLocalRepository().Generic(params)

You can also create a local repository with basic local params:

params := services.NewLocalRepositoryBaseParams()
params.Key = "generic-repo"
params.PackageType = "generic"
params.Description = "This is a public description for generic-repo"
err := servicesManager.CreateLocalRepository(params)

Updating local Generic repository:

err = servicesManager.UpdateLocalRepository().Generic(params)

Creating and Updating Remote Repository

You can create and update a remote repository for the following package types:

Alpine, Bower, Cran, Cargo, Chef, Cocoapods, Composer, Conan, Conda, Debian, Docker, Gems, Generic, Gitlfs, Go, Gradle, Helm, Ivy, Maven, Npm, Nuget, Opkg, P2, Puppet, Pypi, Rpm, Sbt, Swift, Terraform, Vcs, and Yum.

Each package type has its own parameters struct, can be created using the method New<packageType>RemoteRepositoryParams().

Example for creating remote Maven repository:

params := services.NewMavenRemoteRepositoryParams()
params.Key = "maven-central-remote"
params.Url = "https://repo.maven.apache.org"
params.RepoLayoutRef = "maven-2-default"
params.Description = "A caching proxy repository for Maven central"
params.HandleSnapshot = false
params.HandleReleases = true
params.FetchJarsEagerly = true
params.XrayIndex = true
params.AssumedOfflinePeriodSecs = 600
params.SuppressPomConsistencyChecks = true
params.RemoteRepoChecksumPolicyType = "pass-thru"

err = servicesManager.CreateRemoteRepository().Maven(params)

Updating remote Maven repository:

err = servicesManager.UpdateRemoteRepository().Maven(params)

You can also create a remote repository with basic remote params:

params := services.NewRemoteRepositoryBaseParams()
params.Key = "remote-repo"
params.Url = "https://repo.maven.apache.org"
err := servicesManager.CreateRemoteRepository(params)

Creating and Updating Virtual Repository

You can create and update a virtual repository for the following package types:

Alpine, Bower, Cran, Chef, Conan, Conda, Debian, Docker, Gems, Generic, Gitlfs, Go, Gradle, Helm, Ivy, Maven, Npm, Nuget, P2, Puppet, Pypi, Rpm, Sbt, Swift, Terraform and Yum.

Each package type has its own parameters struct, can be created using the method New<packageType>VirtualRepositoryParams().

Example for creating virtual Go repository:

params := services.NewGoVirtualRepositoryParams()
params.Description = "This is an aggregated repository for several go repositories"
params.RepoLayoutRef = "go-default"
params.Repositories = {"gocenter-remote", "go-local"}
params.DefaultDeploymentRepo = "go-local"
params.ExternalDependenciesEnabled = true
params.ExternalDependenciesPatterns = {"**/github.com/**", "**/golang.org/**", "**/gopkg.in/**"}
params.ArtifactoryRequestsCanRetrieveRemoteArtifacts = true

err = servicesManager.CreateVirtualRepository().Go(params)

You can also create a virtual repository with basic virtual params:

params := services.NewVirtualRepositoryBaseParams()
params.Key = "generic-repo"
params.PackageType = "generic"
params.Description = "This is a public description for generic-repo"
params.Repositories = string[]{"remote-repo","local-repo"}
err := servicesManager.CreateVirtualRepository(params)

Updating virtual Go repository:

err = servicesManager.UpdateVirtualRepository().Go(params)

Creating and Updating Federated Repository

You can create and update a federated repository for the following package types:

Alpine, Bower, Cran, Cargo, Chef, Cocoapods, Composer, Conan, Conda, Debian, Docker, Gems, Generic, Gitlfs, Go, Gradle, Helm, Ivy, Maven, Npm, Nuget, Opkg, Puppet, Pypi, Rpm, Sbt, Swift, Terraform, Vagrant and Yum

Each package type has its own parameters struct, can be created using the method New<packageType>FederatedRepositoryParams().

Example for creating federated Generic repository:

params := services.NewGenericFederatedRepositoryParams()
params.Key = "generic-repo"
params.Description = "This is a public description for generic-repo"
params.Notes = "These are internal notes for generic-repo"
params.RepoLayoutRef = "simple-default"
params.ArchiveBrowsingEnabled = true
params.XrayIndex = true
params.IncludesPattern = "**/*"
params.ExcludesPattern = "excludedDir/*"
params.DownloadRedirect = true
params.Members = []services.FederatedRepositoryMemberParams{
		{Url: "http://targetartifactory/artifactory/federatedRepositoryName", Enabled: true},
	}
err = servicesManager.CreateFederatedRepository().Generic(params)

You can also create a federated repository with basic federated params:

params := services.NewFederatedRepositoryBaseParams()
params.Key = "generic-repo"
params.PackageType = "generic"
params.Description = "This is a public description for generic-repo"
params.Members = []services.FederatedRepositoryMemberParams{
		{Url: "http://targetartifactory/artifactory/federatedRepositoryName", Enabled: true},
	}
err := servicesManager.CreateFederatedRepository(params)

Updating federated Generic repository:

err = servicesManager.UpdateFederatedRepository().Generic(params)

Removing a Repository

You can remove a repository from Artifactory using its key:

servicesManager.DeleteRepository("generic-repo")

Getting Repository Details

You can get repository details from Artifactory using its key, and the desired params struct. The function expects to get the repo key (name) and a pointer to a param struct that will be filled up. The param struct should contain the desired params fields corresponded to the Artifactory REST API:

repoDetails = services.RepositoryDetails{}
err := servicesManager.GetRepository("maven-repo", &repoDetails)
repoDetails = services.LocalRepositoryBaseParams{}
err := servicesManager.GetRepository("maven-repo", &repoDetails)
repoDetails = services.MavenLocalRepositoryParams{}
err := servicesManager.GetRepository("maven-repo", &repoDetails)

services.RepositoryDetails

Getting All Repositories

You can get all repositories from Artifactory:

servicesManager.GetAllRepositories()

You can get all repositories from Artifactory filtered according to theirs type and/or theirs package type:

params := services.NewRepositoriesFilterParams()
params.RepoType = "remote"
params.PackageType = "maven"
err := servicesManager.GetAllRepositoriesFiltered(params)

Check if Repository Exists

You can check whether a repository exists in Artifactory:

exists, err := servicesManager.IsRepoExists()

Creating and Updating Repository Replications

Example of creating a repository replication:

params := services.NewCreateReplicationParams()
// Source replication repository.
params.RepoKey = "my-repository"
params.CronExp = "0 0 12 * * ?"
params.Username = "admin"
params.Password = "password"
params.Url = "http://localhost:8081/artifactory/remote-repo"
params.Enabled = true
params.SocketTimeoutMillis = 15000
params.EnableEventReplication = true
params.SyncDeletes = true
params.SyncProperties = true
params.SyncStatistics = true
params.PathPrefix = "/path/to/repo"

err = servicesManager.CreateReplication(params)

Example of updating a local repository replication:

params := services.NewUpdateReplicationParams()
// Source replication repository.
params.RepoKey = "my-repository"
params.CronExp = "0 0 12 * * ?"
params.Enabled = true
params.SocketTimeoutMillis = 15000
params.EnableEventReplication = true
params.SyncDeletes = true
params.SyncProperties = true
params.SyncStatistics = true
params.PathPrefix = "/path/to/repo"

err = servicesManager.UpdateReplication(params)

Getting a Repository Replication

You can get a repository replication configuration from Artifactory using its key:

replicationConfiguration, err := servicesManager.GetReplication("my-repository")

Removing a Repository Replication

You can remove a repository replication configuration from Artifactory using its key:

err := servicesManager.DeleteReplication("my-repository")

Converting a Local Repository to a Federated Repository

You can convert a local repository to a federated repository using its key:

err := servicesManager.ConvertLocalToFederatedRepository("my-repository")

Triggering a Full Federated Repository Synchronisation

You can trigger a full federated repository synchronisation for all members using its key:

err := servicesManager.TriggerFederatedRepositoryFullSyncAll("my-repository")

You can also trigger a full federated repository synchronisation for a specific member using its key and the members URL

err := servicesManager.TriggerFederatedRepositoryFullSyncMirror("my-repository", "http://localhost:8081/artifactory/my-repository")

Creating and Updating Permission Targets

You can create or update a permission target in Artifactory. Permissions are set according to the following conventions: read, write, annotate, delete, manage, managedXrayMeta, distribute For repositories You can specify the name "ANY" in order to apply to all repositories, "ANY REMOTE" for all remote repositories or "ANY LOCAL" for all local repositories.

Creating a new permission target :

params := services.NewPermissionTargetParams()
params.Name = "java-developers"
params.Repo = &services.PermissionTargetSection{}
params.Repo.Repositories = []string{"ANY REMOTE", "local-repo1", "local-repo2"}
params.Repo.ExcludePatterns = []string{"dir/*"}
params.Repo.Actions = &services.Actions{}
params.Repo.Actions.Users = map[string][]string{
    "user1": {"read", "write"},
    "user2": {"write", "annotate", "read"},
}
params.Repo.Actions.Groups = map[string][]string{
    "group1": {"manage", "read", "annotate"},
}
// This is the default value that cannot be changed
params.Build = &services.PermissionTargetSection{}
params.Build.Repositories = []string{"artifactory-build-info"}
params.Build.Actions = &services.Actions{}
params.Build.Actions.Groups = map[string][]string{
    "group1": {"manage", "read", "write", "annotate", "delete"},
    "group2": {"read"},
}

err := testsPermissionTargetService.Create(params)

Updating an existing permission target :

err = servicesManager.UpdatePermissionTarget(params)

Removing a Permission Target

You can remove a permission target from Artifactory using its name:

err = servicesManager.DeletePermissionTarget("java-developers")

Fetching a Permission Target

You can fetch a permission target from Artifactory using its name:

permissionTargetParams, err = servicesManager.GetPermissionTarget("java-developers")

If the requested permission target does not exist, a nil value is returned for the permissionTargetParams param, with a nil error value

Fetching Artifactory's Version

version, err := servicesManager.GetVersion()

Fetching Running Artifactory Nodes in a Cluster

runningNodes, err := servicesManager.GetRunningNodes()

Fetching Artifactory's Service ID

serviceId, err := servicesManager.GetServiceId()

Fetching Artifactory's Config Descriptor

Notice: This API is enabled only on self-hosted Artifactory servers

serviceId, err := servicesManager.GetConfigDescriptor()

Activating Artifactory's Key Encryption

Notice: This API is enabled only on self-hosted Artifactory servers

err := servicesManager.ActivateKeyEncryption()

Deactivating Artifactory's Key Encryption

Notice: This API is enabled only on self-hosted Artifactory servers

wasEncrypted, err := servicesManager.DeactivateKeyEncryption()

Fetching Users Details

params := services.NewUserParams()
params.UserDetails.Name = "myUserName"

user, err := serviceManager.GetUser(params)

If the requested user does not exist, a nil value is returned for the User param, with a nil error value

Fetching All Users Details

You can get all users from Artifactory:

users, err := servicesManager.GetAllUsers()

Creating Inviting and Updating a User

params := services.NewUserParams()
params.UserDetails.Name = "myUserName"
params.UserDetails.Email = "[email protected]"
params.UserDetails.Password = "Password1"
params.UserDetails.Admin = &falseValue
params.UserDetails.Realm = "internal"
params.UserDetails.ProfileUpdatable = &trueValue
params.UserDetails.DisableUIAccess = &falseValue
params.UserDetails.InternalPasswordDisabled = &falseValue
params.UserDetails.groups = []string{"GroupA", "GroupB"}
// Set to true to replace existing user with the same name.
params.ReplaceIfExists = false
err := serviceManager.CreateUser(params)

// Inviting user to the platform.
param.UserDetails.ShouldInvite = &trueValue
param.UserDetails.Source = "cli"
err := serviceManager.CreateUser(params)

params.UserDetails.groups = []string{"GroupA", "GroupB", "GroupC"}
err := serviceManager.UpdateUser(params)

// Set to true to remove a user from every group.
params.UserDetails.ClearGroups = true
err := serviceManager.UpdateUser(params)

Deleting a User

err := serviceManager.DeleteUser("myUserName")

Fetching Locked Out Users

lockedUsers, err := serviceManager.GetLockedUsers()

Unlock Locked Out User

Unlocks a locked out user. This function succeeds even if the user doesn't exist or not locked.

err := serviceManager.UnlockUser("userToUnlock")

Fetching All Groups

You can get all groups from Artifactory

groups, err := serviceManager.GetAllGroups()

Fetching Group Details

params := services.NewGroupParams()
params.GroupDetails.Name = "myGroupName"
// Set this param to true to receive the usernames associated with this group
params.IncludeUsers = true

group, err := serviceManager.GetGroup(params)

If the requested group does not exist, a nil value is returned for the Group param, with a nil error value

Creating and Updating a Group

params := services.NewGroupParams()
params.GroupDetails.Name = "myGroupName"
params.GroupDetails.Description = "Description"
params.GroupDetails.AutoJoin = &falseValue
params.GroupDetails.AdminPrivileges = &trueValue
params.GroupDetails.Realm = "internal"
params.GroupDetails.UsersNames = [2]string{"UserA", "UserB"}
// Set to true in order to replace exist group with the same name
params.ReplaceIfExists = false
err := serviceManager.CreateGroup(params)

params.GroupDetails.Description = "Newer Description"
// Will add UserC to the group (in addition to existing UserA and UserB)
params.GroupDetails.UsersNames = [1]string{"UserC"}

err := serviceManager.UpdateGroup(params)

Deleting a Group

err := serviceManager.DeleteGroup("myGroupName")

Generating Full System Export

params := services.NewExportParams("/tmp/")
err := serviceManager.Export(params)

Getting Info of a Folder in Artifactory

serviceManager.FolderInfo("repo/path/")

Getting Info of a File in Artifactory

serviceManager.FileInfo("repo/path/file")

Getting a listing of files and folders within a folder in Artifactory

optionalParams := servicesutils.NewFileListParams()
optionalParams.Deep=               true
optionalParams.Depth=              2
optionalParams.ListFolders=        true
optionalParams.MetadataTimestamps= true
optionalParams.IncludeRootPath=    true
serviceManager.FileList("repo/path/", optionalParams)

Getting Storage Summary Info of Artifactory

storageInfo, err := serviceManager.GetStorageInfo()

Triggering Storage Info Recalculation in Artifactory

err := serviceManager.CalculateStorageInfo()

Access APIs

Creating Access Service Manager

Creating Access Details

accessDetails := accessAuth.NewAccessDetails()
accessDetails.SetUrl("http://localhost:8081/access/")
accessDetails.SetSshKeyPath("path/to/.ssh/")
accessDetails.SetApiKey("apikey")
accessDetails.SetUser("user")
accessDetails.SetPassword("password")
accessDetails.SetAccessToken("accesstoken")
// if client certificates are required
accessDetails.SetClientCertPath("path/to/.cer")
accessDetails.SetClientCertKeyPath("path/to/.key")

Creating Access Service Config

serviceConfig, err := clientConfig.NewConfigBuilder().
SetServiceDetails(accessAuth).
SetCertificatesPath(certsPath).
SetInsecureTls(accessDetails.InsecureTls).
SetDryRun(isDryRun).
Build()

Creating New Access Service Manager

accessManager, err := access.New(serviceConfig)

Using Access Services

Creating a New Project

adminPriviligies := accessServices.AdminPrivileges{
	ManageMembers:   &trueValue,
	ManageResources: &trueValue,
	IndexResources:  &trueValue,
}
projectDetails := accessServices.Project{
	DisplayName:       "testProject",
	Description:       "My Test Project",
	AdminPrivileges:   &adminPriviligies,
	SoftLimit:         &falseValue,
	StorageQuotaBytes: 1073741825, // needs to be higher than 1073741824
	ProjectKey:        "tstprj",
}
projectParams = accessServices.NewProjectParams()
projectParams.ProjectDetails = projectDetails
err = accessManager.CreateProject(projectParams)

Updating a Project

adminPriviligies := accessServices.AdminPrivileges{
	ManageMembers:   true,
	ManageResources: true,
	IndexResources:  true,
}
projectDetails := accessServices.Project{
	DisplayName:       "testProject",
	Description:       "My Test Project",
	AdminPrivileges:   &adminPriviligies,
	SoftLimit:         &falseValue,
	StorageQuotaBytes: 1073741825, // needs to be higher than 1073741824
	ProjectKey:        "tstprj",
}
projectParams = accessServices.NewProjectParams()
projectParams.ProjectDetails = projectDetails
err = accessManager.UpdateProject(projectParams)

Deleting a Project

err = accessManager.DeleteProject("tstprj")

Getting a Project

err = accessManager.GetProject("tstprj")

Getting all Projects

err = accessManager.GetAllProjects()

Assigning Repository to Project

// Params: (repositoryName, projectKey string, isForce bool)
err = accessManager.AssignRepoToProject("repoName", "tstprj", true)

Un-assigning Repository from Project

err = accessManager.AssignRepoToProject("repoName")

Get all groups assigned to a project

err = accessManager.GetProjectsGroups("tstprj")

Get a specific group assigned to a project

err = accessManager.GetProjectsGroup("tstprj", "tstgroup")

Add or update a group assigned to a project

projectGroup := accessServices.ProjectGroup{
  Name:  "tstgroup",
  Roles: []string{"Contributor","Release Manager"},
}
err = accessManager.UpdateGroupInProject("tstprj", "tstgroup", projectGroup)

Remove a group from a project

err = accessManager.DeleteExistingProjectGroup("tstprj", "tstgroup")

Send Web Login Authentication Request

uuid := "09b34617-b48a-455d-8b05-25a6989fb76a"
err = accessManager.SendLoginAuthenticationRequest(uuid)

Get Web Login Authentication Token

uuid := "09b34617-b48a-455d-8b05-25a6989fb76a"
err = accessManager.GetLoginAuthenticationToken(uuid)

Creating an Access Token

params := CreateTokenParams{}
params.Scope = "applied-permissions/user"
params.Username = "my-user"
params.ExpiresIn = 12345 // nil = system default, 0 = no expiry.
params.Refreshable = true
params.Audience = "jfrt@<serviceID1>"
reference := true
params.IncludeReferenceToken = &reference
params.ProjectKey = "my-project"
params.Description = "my-token"

results, err := accessManager.CreateToken(params)

Refreshing an Access Token

params := accessServices.CreateTokenParams{}
params.RefreshToken = "<refresh token>"

results, err := accessManager.RefreshToken(params)

Distribution APIs

Creating Distribution Service Manager

Creating Distribution Details

distDetails := auth.NewDistributionDetails()
distDetails.SetUrl("http://localhost:8081/distribution")
distDetails.SetSshKeyPath("path/to/.ssh/")
distDetails.SetApiKey("apikey")
distDetails.SetUser("user")
distDetails.SetPassword("password")
distDetails.SetAccessToken("accesstoken")
// if client certificates are required
distDetails.SetClientCertPath("path/to/.cer")
distDetails.SetClientCertKeyPath("path/to/.key")

Creating Distribution Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(rtDetails).
    SetCertificatesPath(certPath).
    SetThreads(threads).
    SetDryRun(false).
    // Add [Context](https://golang.org/pkg/context/)
    SetContext(ctx).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(8).
    Build()

Creating New Distribution Service Manager

distManager, err := distribution.New(serviceConfig)

Using Distribution Services

Setting Distribution Signing Key

params := services.NewSetSigningKeyParams("private-gpg-key", "public-gpg-key")

err := distManager.SetSigningKey(params)

Creating a Release Bundle v1

params := services.NewCreateReleaseBundleParams("bundle-name", "1")
params.Description = "Description"
params.ReleaseNotes = "Release notes"
params.ReleaseNotesSyntax = "plain_text"
targetProps := utils.NewProperties()
targetProps.AddProperty("key1", "val1")
params.SpecFiles = []*utils.CommonParams{{Pattern: "repo/*/*.zip", TargetProps: targetProps}}

// Be default, artifacts that are distributed as part of a release bundle v1, have the same path in their destination server
// (the edge node) as the path they had on the distributing Artifactory server.
// You have however the option for modifying the target path on edge node. You do this by defining the Target property as shown below.
// The Pattern property is a wildcard based pattern. Any wildcards enclosed in parentheses in the pattern (source)
// path can be matched with a corresponding placeholder in the target path, to determine the path and name
// of the artifact, once distributed to the edge node.
// In the following example, the path in the edge node is similar to the path in the source Artifactory server, except for the additional "dir" level at the root of the repository.
// Pattern: my-repo/(*)/a.zip
// Target: my-repo/dir/{1}/a.zip
pathMappingSpec := &utils.CommonParams{Pattern: "source-repo/(a)/(*.zip)", Target: "target-repo/{1}-{2}"}
params.SpecFiles = append(params.SpecFiles, pathMappingSpec)

// In case: params.SignImmediately == true, the summary contain the release bundle v1 details. Otherwise, summary is nil.
summary, err := distManager.CreateReleaseBundle(params)

Updating a Release Bundle v1

params := services.NewUpdateReleaseBundleParams("bundle-name", "1")
params.Description = "New Description"
params.ReleaseNotes = "New Release notes"
params.ReleaseNotesSyntax = "plain_text"
targetProps := utils.NewProperties()
targetProps.AddProperty("key1", "val1")
params.SpecFiles = []*utils.CommonParams{{Pattern: "repo/*/*.zip", TargetProps: targetProps}}

// The Target property defines the target path in the edge node, and can include replaceable in the form of {1}, {2}, ...
// Read more about it in the above "Creating a Release Bundle v1" section.
pathMappingSpec := &utils.CommonParams{Pattern: "source-repo/(a)/(*.zip)", Target: "target-repo/{1}-{2}"}
params.SpecFiles = append(params.SpecFiles, pathMappingSpec)

// In case: params.SignImmediately == true, the summary contain the release bundle v1 details. Otherwise, summary is nil.
summary, err := distManager.UpdateReleaseBundle(params)

Signing a Release Bundle v1

params := services.NewSignBundleParams("bundle-name", "1")
params.GpgPassphrase = "123456"

summary, err := distManager.SignReleaseBundle(params)

Async Distributing a Release Bundle v1

params := distribution.NewDistributeReleaseBundleParams("bundle-name", "1")
distributionRules := distribution.DistributionCommonParams{SiteName: "Swamp-1", "CityName": "Tel-Aviv", "CountryCodes": []string{"123"}}}
params.DistributionRules = []*distribution.DistributionCommonParams{distributionRules}
// Auto-creating repository if it does not exist
autoCreateRepo := true
err := distManager.DistributeReleaseBundle(params, autoCreateRepo)

Sync Distributing a Release Bundle v1

params := distribution.NewDistributeReleaseBundleParams("bundle-name", "1")
distributionRules := distribution.DistributionCommonParams{SiteName: "Swamp-1", "CityName": "Tel-Aviv", "CountryCodes": []string{"123"}}}
params.DistributionRules = []*distribution.DistributionCommonParams{distributionRules}
// Auto-creating repository if it does not exist
autoCreateRepo := true
// Wait up to 120 minutes for the release bundle v1 distribution
err := distManager.DistributeReleaseBundleSync(params, 120, autoCreateRepo)

Getting Distribution Status

params := services.NewDistributionStatusParams()
// Optional parameters:
// If missing, get status for all distributions
params.Name = "bundle-name"
// If missing, get status for all versions of "bundle-name"
params.Version = "1"
// If missing, get status for all "bundle-name" with version "1"
params.TrackerId = "123456789"

status, err := distributeBundleService.GetStatus(params)

Deleting a Remote Release Bundle v1

params := services.NewDeleteReleaseBundleParams("bundle-name", "1")
params.DeleteFromDistribution = true
distributionRules := distribution.DistributionCommonParams{SiteName: "Swamp-1", "CityName": "Tel-Aviv", "CountryCodes": []string{"123"}}}
params.DistributionRules = []*distribution.DistributionCommonParams{distributionRules}
// Set to true to enable sync deletion (the command execution will end when the deletion process ends).
param.Sync = true
// Max minutes to wait for sync deletion.
param.MaxWaitMinutes = 10
err := distManager.DeleteReleaseBundle(params)

Deleting a Local Release Bundle v1

params := services.NewDeleteReleaseBundleParams("bundle-name", "1")

err := distManager.DeleteLocalReleaseBundle(params)

Using ContentReader

Some APIs return a content.ContentReader struct, which allows reading the API's output. content.ContentReader provides access to large amounts of data safely, without loading all of it into the memory. Here's an example for how content.ContentReader should be used:

reader, err := servicesManager.SearchFiles(searchParams)
if err != nil {
    return err
}

// Remove the data file used by the reader.
defer func() {
    if reader != nil {
        err = reader.Close()
    }
}()

// Iterate over the results.
for currentResult := new(utils.ResultItem); reader.NextRecord(currentResult) == nil; currentResult = new(utils.ResultItem)  {
    fmt.Printf("Found artifact: %s of type: %s\n", currentResult.Name, currentResult.Type)
}
if err := resultReader.GetError(); err != nil {
    return err
}

// Resets the reader pointer back to the beginning of the output. Make sure not to call this method after the reader had been closed using ```reader.Close()```
reader.Reset()
  • reader.NextRecord(currentResult) reads the next record from the reader into currentResult of type utils.ResultItem.

  • reader.Close() removes the file used by the reader after it is used (preferably using defer).

  • reader.GetError() returns any error that might have occurred during NextRecord().

  • reader.Reset() resets the reader back to the beginning of the output.

Xray APIs

Creating Xray Service Manager

Creating Xray Details

xrayDetails := auth.NewXrayDetails()
xrayDetails.SetUrl("http://localhost:8081/xray")
xrayDetails.SetSshKeyPath("path/to/.ssh/")
xrayDetails.SetApiKey("apikey")
xrayDetails.SetUser("user")
xrayDetails.SetPassword("password")
xrayDetails.SetAccessToken("accesstoken")
// if client certificates are required
xrayDetails.SetClientCertPath("path/to/.cer")
xrayDetails.SetClientCertKeyPath("path/to/.key")

Creating Xray Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(xrayDetails).
    SetCertificatesPath(certPath).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(8).
    Build()

Creating New Xray Service Manager

xrayManager, err := xray.New(serviceConfig)

Using Xray Services

Fetching Xray's Version

version, err := xrayManager.GetVersion()

Creating an Xray Watch

This uses API version 2.

You are able to configure repositories and builds on a watch. However, bundles are not supported.

params := utils.NewWatchParams()
params.Name = "example-watch-all"
params.Description = "All Repos"
params.Active = true

params.Repositories.Type = utils.WatchRepositoriesAll
params.Repositories.All.Filters.PackageTypes = []string{"Npm", "maven"}
params.Repositories.ExcludePatterns = []string{"excludePath1", "excludePath2"}
params.Repositories.IncludePatterns = []string{"includePath1", "includePath2"}

params.Builds.Type = utils.WatchBuildAll
params.Builds.All.Bin_Mgr_ID = "default"

params.Policies = []utils.AssignedPolicy{
  {
    Name: policy1Name,
    Type: "security",
  },
  {
    Name: policy2Name,
    Type: "security",
  },
}

err := xrayManager.CreateWatch(*params)

Get an Xray Watch

watch, err := xrayManager.GetWatch("example-watch-all")

Update an Xray Watch

watch, err := xrayManager.GetWatch("example-watch-all")
watch.Description = "Updated description"

err := xrayManager.UpdateWatch(*watch)

Delete an Xray Watch

err := xrayManager.DeleteWatch("example-watch-all")

Creating a Security Xray Policy

params := utils.NewPolicyParams()
params.Name = "example-security-policy"
params.Type = utils.Security
params.Description = "Security policy with 2 rules"
params.Rules = []utils.PolicyRule{
params.Rules = []utils.PolicyRule{
	{
		Name:     "min-severity-rule",
		Criteria: *utils.CreateSeverityPolicyCriteria(utils.Low),
		Priority: 1,
	},
	{
		Name:     "cvss-range-rule",
		Criteria: *utils.CreateCvssRangePolicyCriteria(5.7, 8.9),
		Priority: 2,
		Actions: &utils.PolicyAction{
			Webhooks: []string{"sec_webhook"},
			BlockDownload: utils.PolicyBlockDownload{
				Active:    &trueValue,
				Unscanned: &falseValue,
			},
			BlockReleaseBundleDistribution: &falseValue,
			FailBuild:                      &trueValue,
			NotifyDeployer:                 &falseValue,
			NotifyWatchRecipients:          &trueValue,
			CustomSeverity:                 utils.Medium,
		},
	},
}
err := xrayManager.CreatePolicy(params)

Creating a License Xray Policy

params := utils.NewPolicyParams()
params.Name = "example-licence-policy"
params.Type = utils.License
params.Description = "License policy with 2 rules"
params.Rules = []utils.PolicyRule{
	{
		Name:     "allowed-licenses",
		Criteria: *utils.CreateLicensePolicyCriteria(true, true, false, "MIT", "Apache-2.0"),
		Priority: 1,
	},
	{
		Name:     "baned-licenses",
		Criteria: *utils.CreateLicensePolicyCriteria(false, true, false, "GPL"),
		Priority: 2,
	},
}
err := xrayManager.CreatePolicy(params)

Get an Xray Policy

policy, err := xrayManager.GetPolicy("example-policy")

Update an Xray Policy

policy, err := xrayManager.GetPolicy("example-policy")
policy.Description = "Updated description"

err := xrayManager.UpdatePolicy(*policy)

Delete an Xray Policy

err := xrayManager.DeletePolicy("example-policy")

Create an Xray Ignore Rule

params := utils.NewIgnoreRuleParams()
params.Notes := "random-notes-for-ignore-rules"
params.ExpiredAt := time.Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location)
params.IgnoreFilters := IgnoreFilters{
  Vulnerabilities: []string{"XRAY-12345", "XRAY-67891"},
  Licenses: []string{"MIT", "BSD"},
  CVEs: []string{"CVE-2021-1234", "CVE-2022-5678"},
  Policies: []string{"policy-name-1", "policy-name-2"},
  Watches: []string{"watch-name-1", "watch-name-2"},
  DockerLayers: []string{"0503825856099e6adb39c8297af09547f69684b7016b7f3680ed801aa310baaa"},
  OperationalRisks: []string{"any"},
  Exposures: []ExposuresFilterName{
    {
      FilePath: []string{"/path/to/file1", "/path/to/file2"},
      Scanners: []string{"EXP-12345"},
      Catagories: []ExposuresCatagories{
        {
          Secrets: true,
          Services: true,
          Applications: true,
          Iac: true,
        },
      },
    },
  },
  ReleaseBundles: []IgnoreFilterNameVersion{
    {
      Name: "RB-name",
      Version: "0.0.0",
    },
    {
      Name: "RB-name-2",
      Version: "1.2.3",
    },
  },
  Builds: []IgnoreFilterNameVersion{
    {
      Name: "build-name",
      Version: "0.0.0",
    },
    {
      Name: "build-name-2",
      Version: "1.2.3",
    },
  },
  Components: []IgnoreFilterNameVersion{
    {
      Name: "component-name",
      Version: "0.0.0",
    },
    {
      Name: "component-name-2",
      Version: "1.2.3",
    },
  },
  Arti: []IgnoreFilterNameVersion{
    {
      Name: "artifact-name",
      Version: "0.0.0",
    },
    {
      Name: "artifact-name-2",
      Version: "1.2.3",
    },
  },
}

ignoreRuleIgnoreId, err := xrayManager.CreateIgnoreRule(params)

Get an Xray Ignore Rule

ignoreRule, err := xrayManager.GetIgnoreRule("ignore-rule-id")

Delete an Xray Ignore Rule

err := xrayManager.DeleteIgnoreRule("ignore-rule-id")

Add Builds to Indexing Configuration

buildsToIndex := []string{"buildName1", "buildName2"}
err := xrayManager.AddBuildsToIndexing(buildsToIndex)

Request Graph Scan

graphScanParams := &XrayGraphScanParams{}
// Dependency tree. Each node must have a component identifier, see https://www.jfrog.com/confluence/display/JFROG/Xray+REST+API#XrayRESTAPI-ComponentIdentifiers.
graphScanParams.Graph = &GraphNode{
  Id: "gav://org.jfrog.buildinfo:build-info-extractor-gradle:4.24.5",
  Nodes: []*GraphNode{{Id: "gav://junit:junit:4.13.2"}, {Id: "gav://commons-lang:commons-lang:2.6"}}}
scanId, err := xrayManager.ScanGraph(graphScanParams)

Retrieve the Graph Scan Results

// scanId should be received from xrayManager.ScanGraph(graphScanParams) request.
scanResults, err := xrayManager.GetScanGraphResults(scanId)

Request Graph Enrich

graphImportParams := &XrayGraphImportParams{}
// Dependency tree. Each node must have a component identifier, see https://www.jfrog.com/confluence/display/JFROG/Xray+REST+API#XrayRESTAPI-ComponentIdentifiers.
graphScanParams.SBOMInput = "{
  "bomFormat": "CycloneDX",
  "specVersion": "1.4",
  "serialNumber": "urn:uuid:3c94db59-0dbf-41cd-49e8-c4518ac2ef3c",
  "version": 1,
  "metadata": {
    "timestamp": "2024-05-22T14:52:40Z",
    "tools": [
      {
      "vendor": "JFrog Inc.",
      "name": "Xray",
      "version": "3.95.7"
      }
    ],
    "component": {
      "type": "container",
      "name": "jfrog/artifactory-pro:sha256",
      "version": "2e774ffb112bcaef62804d97e6db3dc67b9169b440838b12ba12584cba2c5251"
	}
  },
  "components": [
    {
    "bom-ref": "pkg:Oci/jfrog%2Fartifactory-pro:sha256@2e774ffb112bcaef62804d97e6db3dc67b9169b440838b12ba12584cba2c5251",
    "type": "application",
    "name": "jfrog/artifactory-pro:sha256",
    "version": "2e774ffb112bcaef62804d97e6db3dc67b9169b440838b12ba12584cba2c5251",
    "hashes": [
      {
      "alg": "SHA-256",
      "content": "2e774ffb112bcaef62804d97e6db3dc67b9169b440838b12ba12584cba2c5251"
      }
    ],
    "licenses": [],
    "purl": "pkg:Oci/jfrog%2Fartifactory-pro:sha256@2e774ffb112bcaef62804d97e6db3dc67b9169b440838b12ba12584cba2c5251"
    }
  ],
  "dependencies": []
  }

"
scanId, err := xrayManager.ImportGraph(graphImportParams)

Retrieve the Graph Enrich Results

// scanId should be received from xrayManager.ImportGraph(graphImportParams) request.
enrichResults, err := xrayManager.GetImportGraphResults(scanId)

Generate Vulnerabilities Report

vulnerabilitiesReportRequest := services.VulnerabilitiesReportRequestParams{
  Name: "example-report",
  Filters: services.VulnerabilitiesFilter{
    VulnerableComponent: "*vulnerable:component*",
    ImpactedArtifact: "some://impacted*artifact",
    HasRemediation: &falseValue,
    Cve: "CVE-1234-1234",
    IssueId: "XRAY-1234",
    Severity: []string{
        "High",
        "Medium"
      },
    CvssScore: services.CvssScore {
        MinScore: float64(6.3),
        MaxScore: float64(9)
    },
    Published: services.DateTimeRange {
        Start: "2020-06-29T12:22:16Z",
        End: "2020-06-29T12:22:16Z"
    },
    ScanDate: services.DateTimeRange {
        Start: "2020-06-29T12:22:16Z",
        End: "2020-06-29T12:22:16Z"
    }
  },
  Resources: services.Resource{
    IncludePathPatterns: []string{ "/example-sub-dir/**" },
    Repositories: []services.Repository{
      {
        Name: "example-repository",
      },
    },
  },
}

// The reportRequestResponse will contain the report ID to use in subsequent requests
reportRequestResponse, err := xrayManager.GenerateVulnerabilitiesReport(vulnerabilitiesReportRequest)

Get Vulnerabilities Report Details

// The reportId argument value is returned as part of the xrayManager.GenerateVulnerabilitiesReport API response.
reportDetails, err := xrayManager.ReportDetails(reportId)

Get Vulnerabilities Report Content

// The ReportId value is returned as part of the xrayManager.GenerateVulnerabilitiesReport API response.
reportContentRequest := services.ReportContentRequestParams{
  ReportId:  "example-report-id",
  Direction: "asc",
  PageNum:   0,
  NumRows:   0,
  OrderBy:   "severity",
}
reportContent, err := xrayManager.ReportContent(reportContentRequest)

Delete Vulnerabilities Report

// The reportId argument value is returned as part of the xrayManager.GenerateVulnerabilitiesReport API response.
err := xrayManager.DeleteReport(reportId)

Generate Licences Report

licensesReportRequest := services.LicensesReportRequestParams{
  Name: "example-report",
  Filters: services.LicensesFilter{
    Component: "*gav:component*",
    Artifact: "some://impacted*artifact",
    Unknown: &falseValue,
    Unrecognized: &trueValue,
    LicenseNames: []string{
        "Apache",
        "MIT",
        "AFL"
    },
    LicensePatterns: []string{
        "*Apache*",
        "The Apache*",
        "AFL*"
    },
    ScanDate: services.DateTimeRange {
        Start: "2020-06-29T12:22:16Z",
        End: "2020-06-29T12:22:16Z"
    }
  },
  Resources: services.Resource{
    IncludePathPatterns: []string{ "/example-sub-dir/**" },
    Repositories: []services.Repository{
      {
        Name: "example-repository",
      },
    },
  },
}

// The reportRequestResponse will contain the report ID to use in subsequent requests
reportRequestResponse, err := xrayManager.GenerateLicencesReport(licensesReportRequest)

Get Licences Report Details

// The reportId argument value is returned as part of the xrayManager.GenerateLicencesReport API response.
reportDetails, err := xrayManager.ReportDetails(reportId)

Get Licences Report Content

// The ReportId value is returned as part of the xrayManager.GenerateLicencesReport API response.
reportContentRequest := services.ReportContentRequestParams{
  ReportId:  "example-report-id",
  Direction: "asc",
  PageNum:   0,
  NumRows:   0,
  OrderBy:   "severity",
}
reportContent, err := xrayManager.ReportContent(reportContentRequest)

Delete Licences Report

// The reportId argument value is returned as part of the xrayManager.GenerateLicencesReport API response.
err := xrayManager.DeleteReport(reportId)

Generate Violations Report

violationsReportRequest := services.ViolationsReportRequestParams{
  Name: "example-report",
  Filters: 		Type: "security|license|operational_risk",
    WatchNames: []string{
      "NameOfWatch1",
      "NameOfWatch2"
    },
    WatchPatterns: []string{
      "WildcardWatch*"
    },
    Component: "*vulnerable:component*",
    Artifact: "some://impacted*artifact",
    PolicyNames: []string{
      "NameOfPolicy"
    },
    Severities: []string{
      "High",
      "Medium"
    },
    Updated: services.DateTimeRange {
      Start: "2020-01-02T15:00:00Z",
      End: "2020-12-15T00:00:00Z"
    },
    SecurityFilters: services.VulnerabilitiesFilter{
      Cve: "CVE-2020-10693",
      IssueId: "XRAY-87343",
      Severity: []string{
          "High",
          "Medium"
        },
      CvssScore: services.CvssScore {
          MinScore: float64(6.3),
          MaxScore: float64(9)
      },
      Published: services.DateTimeRange {
          Start: "2020-06-29T12:22:16Z",
          End: "2020-06-29T12:22:16Z"
      },
      ScanDate: services.DateTimeRange {
          Start: "2020-06-29T12:22:16Z",
          End: "2020-06-29T12:22:16Z"
      },
      SummaryContains: "kernel",
      HasRemediation: &falseValue,
    },
    LicenseFilters: services.LicensesFilter {
      Unknown: &falseValue,
      Unrecognized: &trueValue,
		LicenseNames: []string{
	    "Apache",
	    "MIT",
	    "AFL"
		},
    LicensePatterns: []string{
      "*Apache*",
      "AFL*"
    },
  }
  Resources: services.Resource{
    IncludePathPatterns: []string{ "/example-sub-dir/**" },
    Repositories: []services.Repository{
      {
        Name: "example-repository",
      },
    },
  },
}

// The reportRequestResponse will contain the report ID to use in subsequent requests
reportRequestResponse, err := xrayManager.GenerateViolationsReport(violationsReportRequest)

Get Violations Report Details

// The reportId argument value is returned as part of the xrayManager.GenerateViolationsReport API response.
reportDetails, err := xrayManager.ReportDetails(reportId)

Get Violations Report Content

// The ReportId value is returned as part of the xrayManager.GenerateViolationsReport API response.
reportContentRequest := services.ReportContentRequestParams{
  ReportId:  "example-report-id",
  Direction: "asc",
  PageNum:   0,
  NumRows:   0,
  OrderBy:   "severity",
}
reportContent, err := xrayManager.ReportContent(reportContentRequest)

Delete Violations Report

// The reportId argument value is returned as part of the xrayManager.GenerateViolationsReport API response.
err := xrayManager.DeleteReport(reportId)

Get Artifact Summary

artifactSummaryRequest := services.ArtifactSummaryParams{
  Checksums: []string{"a96370b18b3d7e70b7b34d49dcb621a805c15cf71217ee8c77be5a98cc793fd3"},
  Paths:     []string{"default/example-repository/example-folder/example-artifact"},
}
artifactSummary, err := xrayManager.ArtifactSummary(artifactSummaryRequest)

Get Entitlement Info

    // The featureId is the requested feature ID to check, for instance: "contextual_analysis"
    isEntitled, err := xrayManager.IsEntitled(featureId)

XSC APIs

Creating XSC Service Manager

Creating XSC Details

xscDetails := auth.NewXscDetails()
xscDetails.SetUrl("http://localhost:8081/xsc")
xscDetails.SetSshKeyPath("path/to/.ssh/")
xscDetails.SetApiKey("apikey")
xscDetails.SetUser("user")
xscDetails.SetPassword("password")
xscDetails.SetAccessToken("accesstoken")
// if client certificates are required
xscDetails.SetClientCertPath("path/to/.cer")
xscDetails.SetClientCertKeyPath("path/to/.key")

Creating XSC Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(xscDetails).
    SetCertificatesPath(certPath).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(8).
    Build()

Creating New XSC Service Manager

xscManager, err := xsc.New(serviceConfig)

Using XSC Services

Fetching XSC's Version

version, err := xscManager.GetVersion()

Report XSC analytics metrics

Add analytics general event

Sent XSC a new event which contains analytics data, and get multi-scan id back from XSC.

event := services.XscAnalyticsGeneralEvent{
	XscAnalyticsBasicGeneralEvent: services.XscAnalyticsBasicGeneralEvent{
            EventType:              services.CliEventType,
            Product:                services.CliProduct,
            ProductVersion:         "2.53.1",
            IsDefaultConfig:        false,
            JfrogUser:              "gail",
            OsPlatform:             "mac",
            OsArchitecture:         "arm64",
            AnalyzerManagerVersion: "1.1.1",
            EventStatus:            services.Started,
}}
msi, err := xscManager.AddAnalyticsGeneralEvent(event)
Update analytics general event

Sent XSC a finalized analytics metrics event with information matching an existing event's msi.

finalizeEvent := services.XscAnalyticsGeneralEventFinalize{
    MultiScanId: msi,
    XscAnalyticsBasicGeneralEvent: services.XscAnalyticsBasicGeneralEvent{
        EventStatus:          services.Completed,
        TotalFindings:        10,
        TotalIgnoredFindings: 5,
        TotalScanDuration:    "15s",
    },
}
err := xscManager.UpdateAnalyticsGeneralEvent(finalizeEvent)
Get analytics general event

Get a general event from XSC matching the provided msi.

event, err := xscManager.GetAnalyticsGeneralEvent(msi)

Pipelines APIs

Creating Pipelines Service Manager

Creating Pipelines Details

pipelinesDetails := auth.NewPipelinesDetails()
pipelinesDetails.SetUrl("http://localhost:8081/pipelines")
pipelinesDetails.SetAccessToken("accesstoken")
// if client certificates are required
pipelinesDetails.SetClientCertPath("path/to/.cer")
pipelinesDetails.SetClientCertKeyPath("path/to/.key")

Creating Pipelines Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(pipelinesDetails).
    SetCertificatesPath(pipelinesDetails.GetClientCertPath()).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(8).
    Build()

Creating New Pipelines Service Manager

pipelinesManager, err := pipelines.New(serviceConfig)

Using Pipelines Services

Fetching Pipelines' System Info

systemInfo, err := pipelinesManager.GetSystemInfo()

Creating GitHub Integration

id, err := pipelinesManager.CreateGithubIntegration("integrationName", "token")

Creating GitHub Enterprise Integration

id, err := pipelinesManager.CreateGithubEnterpriseIntegration("integrationName", "url", "token")

Creating Bitbucket Integration

id, err := pipelinesManager.CreateBitbucketIntegration("integrationName", "username", "token")

Creating Bitbucket Server Integration

id, err := pipelinesManager.CreateBitbucketServerIntegration("integrationName", "url", "username", "passwordOrToken")

Creating Gitlab Integration

id, err := pipelinesManager.CreateGitlabIntegration("integrationName", "url", "token")

Creating Artifactory Integration

id, err := pipelinesManager.CreateArtifactoryIntegration("integrationName", "url", "username", "apikey")

Get Integration by Id

integrationId := 1234
integration, err := pipelinesManager.GetIntegrationById(integrationId)

Get Integration by Name

integration, err := pipelinesManager.GetIntegrationByName("integrationName")

Get All Integrations

integrations, err := pipelinesManager.GetAllIntegrations()

Get All Raw Integrations

integrations, err := pipelinesManager.GetAllRawIntegrations()

Delete Integration

integrationId := 1234
err := pipelinesManager.DeleteIntegration(integrationId)

Add Pipeline Source

projectIntegrationId := 1234
err := pipelinesManager.AddSource(projectIntegrationId, "domain/repo", "master", "pipelines.yml", "pipelineSourceName")

Get Recent Pipeline Run Status

branch := "master"
pipeline := "pipeline_name"
response, err := pipelinesManager.GetPipelineRunStatusByBranch(branch, pipeline)

Trigger Pipeline Run

branch := "master"
pipeline := "pipeline_name"
status, err := pipelinesManager.TriggerPipelineRun(branch, pipeline)

Trigger Pipeline Sync

branch := "master"
repoName := "jfrog/pipelines" // repository full path
err := pipelinesManager.SyncPipelineResource(branch, repoFullName)

Get Pipeline Sync Status

branch := "master"
repoName := "jfrog/pipelines" // repository full path
err := pipelinesManager.GetSyncStatusForPipelineResource(branch, repoFullName)

Cancel Run

runID := 234 // run id of pipeline
err := pipelinesManager.CancelRun(runID)

Lifecycle APIs

Creating Lifecycle Service Manager

Creating Lifecycle Details

lcDetails := auth.NewLifecycleDetails()
lcDetails.SetUrl("http://localhost:8081/lifecycle")
lcDetails.SetAccessToken("access-token")
// if client certificates are required
lcDetails.SetClientCertPath("path/to/.cer")
lcDetails.SetClientCertKeyPath("path/to/.key")

Creating Lifecycle Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(lcDetails).
    SetCertificatesPath(lcDetails.GetClientCertPath()).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(8).
    Build()

Creating New Lifecycle Service Manager

lifecycleManager, err := lifecycle.New(serviceConfig)

Using Lifecycle Services

Creating a Release Bundle From AQL

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

// The GPG/RSA key-pair name given in Artifactory.
signingKeyName = "key-pair"

aqlQuery := `items.find({"repo": "my-repo","path": ".","name": "a2.in"})`
serviceManager.CreateReleaseBundleFromAql(rbDetails, queryParams, signingKeyName, aqlQuery)

Creating a Release Bundle From Artifacts

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

// The GPG/RSA key-pair name given in Artifactory.
signingKeyName = "key-pair"

artifacts := CreateFromArtifacts{Artifacts: []ArtifactSource{
	{
        Path:   "repo/path/file",
        Sha256: "3e3deb6628658a48cf0d280a2210211f9d977ec2e10a4619b95d5fb85cb10450",
	},
}}

serviceManager.CreateReleaseBundleFromArtifacts(rbDetails, queryParams, signingKeyName, artifacts)

Creating a Release Bundle From Published Builds

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

// The GPG/RSA key-pair name given in Artifactory.
signingKeyName = "key-pair"

source := CreateFromBuildsSource{Builds: []BuildSource{
    {
        BuildName:       "name",
        BuildNumber:     "number",
		// Optional:
        BuildRepository: "artifactory-build-info",
    },
}}
serviceManager.CreateReleaseBundleFromBuilds(rbDetails, queryParams, signingKeyName, source)

Creating a Release Bundle From Release Bundles

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

// The GPG/RSA key-pair name given in Artifactory.
signingKeyName = "key-pair"

source := CreateFromReleaseBundlesSource{ReleaseBundles: []ReleaseBundleSource{
    {
       ReleaseBundleName:    "name",
       ReleaseBundleVersion: "version",
       ProjectKey:           "default",
    },
}}
serviceManager.CreateReleaseBundleFromBundles(rbDetails, params, signingKeyName, source)

Promoting a Release Bundle

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

// The GPG/RSA key-pair name given in Artifactory.
signingKeyName = "key-pair"

promotionParams := RbPromotionParams{}
promotionParams.Environment := "target-env"
promotionParams.IncludedRepositoryKeys := []string{"generic-local"}

resp, err := serviceManager.PromoteReleaseBundle(rbDetails, queryParams, signingKeyName, promotionParams)

Get Release Bundle Creation Status

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
sync := true
// Optional:
projectKey := "default"
resp, err := serviceManager.GetReleaseBundleCreationStatus(rbDetails, projectKey, sync)

Get Release Bundle Promotion Status

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
createdMillis := "1668073165322"
sync := true
// Optional:
projectKey := "default"
resp, err := serviceManager.GetReleaseBundlePromotionStatus(rbDetails, projectKey, createdMillis, sync)

Get Release Bundle Promotions

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}

optionalQueryParams := lifecycle.GetPromotionsOptionalQueryParams{
    Include:    "MSG",
    Offset:     1,
    Limit:      10,
    FilterBy:   "DEV",
    OrderBy:    "created",
    OrderAsc:   true,
    ProjectKey: "default",
}

resp, err := serviceManager.GetReleaseBundleVersionPromotions(rbDetails, optionalQueryParams)

Get Release Bundle Specification

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
resp, err := serviceManager.GetReleaseBundleSpecification(rbDetails)

Distribute Release Bundle

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
pathMappings := []services.PathMapping{
    {
        Pattern: "(*)/(*)",
        Target:  "{1}/target/{2}",
    },
}

rules := &distribution.DistributionCommonParams{
    SiteName:     "*",
    CityName:     "*",
    CountryCodes: []string{"*"},
}
dsParams := DistributeReleaseBundleParams{
    Sync:           true,
    AutoCreateRepo: true,
    MaxWaitMinutes: 60,
    PathMappings:   pathMappings,
    DistributionRules: []*dmUtils.DistributionCommonParams{
        rules,
    },
}

resp, err := serviceManager.DistributeReleaseBundle(rbDetails, dsParams)

Export Release Bundle Archive

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
// Specifies the path mapping for the artifacts
// in the form of input and output regular expressions.
// These expressions define where the artifacts are located
// and where they should be exported.
modifictions:= []utils.PathMapping{{
            Input:  "(.*)/(.*)",
            Output: "$1/mapping/$2",
}}
// Export & Download
res,err:= serviceManager.ExportReleaseBundle(rbDetails, modifications, queryParams)

Import Release Bundle Archive

// Imports an exported release bundle archive
res,err:= serviceManager.releaseService.ImportReleaseBundle(filePath)

Delete Release Bundle Version

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

resp, err := serviceManager.DeleteReleaseBundleVersion(rbDetails, queryParams)

Delete Release Bundle Version Promotion

rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}

queryParams := CommonOptionalQueryParams{}
queryParams.ProjectKey = "project"
queryParams.Async = true

created := "1708612052952"
resp, err := serviceManager.DeleteReleaseBundleVersionPromotion(rbDetails, queryParams, created)

Remote Delete Release Bundle

rules := &distribution.DistributionCommonParams{
    SiteName:     "*",
    CityName:     "*",
    CountryCodes: []string{"*"},
}
params := distribution.NewDistributeReleaseBundleParams("rbName", "rbVersion")
params.DistributionRules = append(params.DistributionRules, rules)

dryRun := true

resp, err := serviceManager.RemoteDeleteReleaseBundle(params, dryRun)

Evidence APIs

Creating Evidence Service Manager

Creating Evidence Details

evdDetails := auth.NewEvidenceDetails()
evdDetails.SetUrl("http://localhost:8081/evidence")
evdDetails.SetAccessToken("access-token")
// if client certificates are required
evdDetails.SetClientCertPath("path/to/.cer")
evdDetails.SetClientCertKeyPath("path/to/.key")

Creating Evidence Service Config

serviceConfig, err := config.NewConfigBuilder().
    SetServiceDetails(evdDetails).
    SetCertificatesPath(evdDetails.GetClientCertPath()).
    // Optionally overwrite the default HTTP retries, which is set to 3.
    SetHttpRetries(3).
    Build()

Creating New Evidence Service Manager

evidenceManager, err := evidence.New(serviceConfig)

Using Evidence Services

Upload Evidence

envelopeBytes := []byte("envelope")

evidenceDetails := evidenceService.EvidenceDetails{
  SubjectUri:  "subjectUri",
  DSSEFileRaw: &envelopeBytes,
}
body, err = evideceManager.UploadEvidence(evidenceDetails)

jfrog-client-go's People

Contributors

alexeivainshtein avatar asafambar avatar asafgabai avatar attiasas avatar barbelity avatar bhanurp avatar dimanevelev avatar eranturgeman avatar eyalb4doc avatar eyalbe4 avatar eyaldelarea avatar freddy33 avatar gailazar300 avatar galusben avatar github-actions[bot] avatar josh-barker-coles avatar liron-shalom avatar ns-kbhat avatar omerzi avatar or-geva avatar orz25 avatar robinino avatar sarao1310 avatar sebinjohn avatar stevenewson avatar sverdlov93 avatar talarian1 avatar tamirhadad avatar yahavi avatar yoav avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jfrog-client-go's Issues

Error in release 0.13.3 undefined: archiver.MatchingFormat

Hello,

There are a missing variable in httpclient :

# github.com/jfrog/jfrog-client-go/httpclient
$GOPATH/pkg/mod/github.com/jfrog/[email protected]/httpclient/client.go:311:10: undefined: archiver.MatchingFormat
$GOPATH/pkg/mod/github.com/jfrog/[email protected]/httpclient/client.go:386:12: arch.Read undefined (type archiver.Archiver has no field or method Read)
$GOPATH/pkg/mod/github.com/jfrog/[email protected]/httpclient/client.go:400:20: invalid method expression archiver.Zip.Open (needs pointer receiver: (*archiver.Zip).Open)
$GOPATH/pkg/mod/github.com/jfrog/[email protected]/httpclient/client.go:623:10: undefined: archiver.MatchingFormat

Thanks in advance for the fix.

The Xray APIs should not use ArtifactoryClientBuilder

Describe the bug
Following the merge of #211 we should refactor the code, so that the new Xray APIs introduced in this PR do not use the ArtifactoryClientBuilder. We should either create a generic builder for all JFrog services, or alternatively create client APIs for Xray.
See the following comment as part of the above PR:

Now that ArtifactoryClientBuilder also needs to be used for Xray, we should rename it to HttpClientBuilder.
We should also move the httpclient package, which is currently located under the artifactory directory, to be one level up, next to the artifactory dir.
rthttpclient should be renamed to httpclient, or if the the httpclient is already taken as an alias in the file, maybe jfroghttpclient.

To Reproduce
N/A

Expected behavior
N/A

Screenshots
N/A

Versions

  • JFrog Go client version: N/A
  • JFrog Go client operating system: N/A
  • Artifactory version: N/A

Is searching for files in Artifactory possible using a Regex?

Hello,

I'm trying to use a regex when searching for files in Artifactory, but not getting any results. I've used:

searchParams := services.NewSearchParams()
searchParams.Regex = true
searchParams.Pattern = "greenlake-centos-7-7-([0-9]*).ova"
resultitems, err := rtManager.SearchFiles(searchParams)

To match files such as:

greenlake-centos-7-7-154678.ova

But I don't get any matches. What have I done wrong? Are regexes supported for SearchFiles().

Thanks.

-Eamonn.

--interactive=false not working

Using the latest and greatest it's telling me that --interactive is telling me that the flag is provided but not defined?

./jfrog rt ping --interactive=false --user=admin --password=admin --url=http://some-artifactory-instance

Any thoughts?

CreateTempDirPath is not called

I tried following the examples in the README to download an artifact but kept getting the following error:

[Error] [Thread 2] Received an error: Function cannot be used before 'tempDirPath' is created.

I had to call fileutils.CreateTempDirPath() before the download works.

Please update the documentation or update the flow to call fileutils.CreateTempDirPath() within DownloadFiles.

The documentation is limited so would be best if you create an end to end example/test using
ArtifactoryServicesManager.

Add context.Context management

Hello,

Unless I'm mistaken, your client has no context.Context management.
Also, would you be able to expose a method to define the context.Context and thus manage the propagation of cancel, timeout, ...?

Thank you in advance.

Regards.

GetAllRepositories does not return repository type information

Artifactory REST API /api/repositories (https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API#ArtifactoryRESTAPI-GetRepositories) response includes the repository type information (LOCAL, REMOTE, VIRTUAL) which is not included in the GetAllRepositories return value. Is this just an oversight or an intentional decision?

Looking at the code it should be trivial to fix by adding 'Type' field to RepositoryDetails struct in getrepository.go.

My use case for this would be to first get all the repositories configured in Artifactory and filter the results based on the repository type and/or package type before getting the full details of the selected repositories.

Add support for client certificates

In many corporate environments, client certificates are used in addition to classic user credentials (dont blame the messenger).

It would thus be nice to have support in the jfrog clients for client certificates.

Allow to set *http.Client in Service Config Builder

Hello,

Unless I am mistaken, it is not possible with your lib to specify the complete configuration of the http client that you want. For example, defining the context, ciphers, curved, dialer, ... is not possible.

Also, would you be able to expose a method to set the * http.Client pointer and not just some sporadic options?

Thank you in advance.

Regards.

Cannot follow example as UploadParams is not correctly initialized by NewUploadParams

In order to properly initialize UploadParams, it must include a new instance of the substructure included only as a reference within UploadParams.

Without it, you get a SIGSEGV when trying to write to uninitialized memory for eg.
Pattern or ExcludePatterns

However, the code is:

    func NewUploadParams() UploadParams {
    	return UploadParams{}
    }

Shorten URL when uploading as archive with props

Is your feature request related to a problem? Please describe.
When uploading files as archive using a spec, with multiple groups having the same target, the archive in Artifactory will have the props of all of the relevant groups in the spec. It's done by concatenating all of the props to the URL, without omitting identical props (Artifactory does that for us). That might cause the UR be very long, and possibly pass the limit of the URL length.

Describe the solution you'd like to see
Omit identical props.

Add ability to set a custom http.Client

Is your feature request related to a problem? Please describe.
I'm using jfrog-client-go to build a tool that allows us to sync artifacts to artifactory servers behind a bastion host. One of the things we need is the ability to set Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)} in the http.Client. It would also be nice if we could completely configure the http.Client so we could use a tool like https://github.com/seborama/govcr for recording & playback of HTTP requests/responses during testing.

Describe the solution you'd like to see
a SetHttpClient(client *http.Client) method on JfrogHttpClient and HttpClient classes.

Describe alternatives you've considered
Setting the http_proxy environmental variable is not sufficient for us, we need to be able to configure the HTTP Proxy used by the Artifactory client on demand.

Behavior of "TargetPath" for "DownloadVersion" is unclear

When using the DownloadVersion function for a package version with multiple files, the behavior of DownloadVersionParams.TargetPath is unintuitive. The observed behavior is the following:

  1. If TargetPath is the path to an existing directory that does not end in / (for example, target/path), then no downloaded files are saved
  2. If TargetPath is a path to a file that does not exist in a directory (for example, target/path/foo), then only a single file is downloaded, and the file is given the specified name (for example, target/path/com/org/product/version/foo)
  3. If TargetPath is a path to an existing directory that ends in / (for example, target/path/), then all version files are downloaded properly

It appears that (3) is the only behavior that is correct -- the behavior of (1) and (2) is very confusing and hard to reason about.

I think the better behavior would be for (1) and (3) to behave the same (basically, check if the output target is an existing directory and, if it is, download version files into the directory), and (2) should raise some kind of error rather than downloading all files in a way that overwrite each other.

Setting / deleting props API should be simplified

It looks like the SetProps and DeleteProps props APIs require using the SearchFiles API. The SetProps and DeleteProps API expect to receive to result of the SearchFiles.
This is both inconsistent with the other APIs (which are all independent) and also cumbersome to use.
In addition, the README is incorrect, because it notes that propsParams includes a Pattern property, while it doesn't, right now, although it should.

No returning error on UploadFiles

Using the examples documented on the README.md to upload a files you can upload files but if there's an error it's not returned just logged.

Environment

go version go1.14.2 windows/amd64
Lib version 0.9.1
Personal Artifactory server

How to reproduce the error:

package main
import (
        "os"
        "fmt"

        "github.com/jfrog/jfrog-client-go/artifactory/auth"
        "github.com/jfrog/jfrog-client-go/config"
        "github.com/jfrog/jfrog-client-go/artifactory"
        "github.com/jfrog/jfrog-client-go/artifactory/services"
        "github.com/jfrog/jfrog-client-go/utils/log"
)

func main() {
    log.SetLogger(log.NewLogger(log.INFO, os.Stdout))
    rtDetails := auth.NewArtifactoryDetails()
    rtDetails.SetUrl("http://localhost:8081/artifactory")
    rtDetails.SetUser("wronguser")
    rtDetails.SetPassword("password")
    serviceConfig, err := config.NewConfigBuilder().
        SetArtDetails(rtDetails).Build()
    if err != nil {
        fmt.Printf("%v", err)
        return
    }

    rtManager, err := artifactory.New(&rtDetails, serviceConfig)
    params := services.NewUploadParams()
    params.Pattern = "/tmp/path"
    params.Target = "test/test/"
    params.Recursive = true
    params.IncludeDirs = false
    params.Regexp = false
    params.Flat = true
    info, _, _, err := rtManager.UploadFiles(params)
    if err != nil {
        fmt.Printf("Should print this error %v", err)
        return
    }
    fmt.Printf("%v", info)
}

Expected behavior:

$ go run main.go
[Info] [Thread 0] Uploading artifact: /tmp/test/test2
[Info] [Thread 2] Uploading artifact: /tmp/test/test.7z
[Error] [Thread 2] Artifactory response: 401 Unauthorized
[Error] [Thread 0] Artifactory response: 401 Unauthorized
[Error] Failed uploading 2 artifacts.
Should print this error Artifactory response: 401 Unauthorized

Actual behavior:

$ go run main.go
[Info] [Thread 0] Uploading artifact: /tmp/test/test2
[Info] [Thread 2] Uploading artifact: /tmp/test/test.7z
[Error] [Thread 2] Artifactory response: 401 Unauthorized
[Error] [Thread 0] Artifactory response: 401 Unauthorized
[Error] Failed uploading 2 artifacts.
[]

The order will not match for the threads, but in order to do error handling the error should be returned if there's one.

Is there a way to set Destination when creating a permission

Hello,

In artifactory UI, when creating a permission there is a Destination section.
I don"t found in the go client where i can manipulate this data (create/update/delete).

The structure PermissionTargetParams do not contains the Destination section

// Using struct pointers to keep the fields null if they are empty.
// Artifactory evaluates inner struct typed fields if they are not null, which can lead to failures in the request.
type PermissionTargetParams struct {
	Name          string                   `json:"name"`
	Repo          *PermissionTargetSection `json:"repo,omitempty"`
	Build         *PermissionTargetSection `json:"build,omitempty"`
	ReleaseBundle *PermissionTargetSection `json:"releaseBundle,omitempty"`
}

There is another way to do this? or it's just missing for now and will be added in the future ?

go test ./tests depends on Artifactory Pro instance

When I execute go test ./tests on master branch it is failing with the following errors

$ go test ./tests/
[Debug] Sending HTTP GET request to: http://localhost:8081/artifactory/api/repositories/jfrog-client-tests-repo1
[Error] Get http://localhost:8081/artifactory/api/repositories/jfrog-client-tests-repo1: EOF
FAIL    github.com/jfrog/jfrog-client-go/tests  0.023s
FAIL

Looking at the Tests section in README, it appears that an Artifactory Pro instance must be running.
This dependency can't be satisfied all the time. Can this dependency be avoided in tests?

SNAT exhaustion on Azure / Github Actions

Hey all,

It appears that due to the leaveBodyOpen style functions, ports aren't getting reused and causing SNAT port exhaustion when being run w/ a large number of requests on AzDO or GHA.

Specifically, I came across this while trying to run a npm install from the jfrog cli, w/ build metadata enabled. Since there is an individual AQL call made for every single dependency to retrieve the checksum for said dependency - it quickly saturates the ports and causes a TCP timeout.

I had looked @ fixing this by writing 'better' AQL in the jfrog CLI, but it appears to cause a segfault on the Artifactory server side due to payload being too large. So instead I started tracking down the network calls, and was led here.

A brief search thru the git history didn't appear to turn up anything -- I'm wondering what the use case for the 'leave body open' connections is? An easy fix would be to not use it - this is certainly a hindrance for any NPM style builds w/ build-info enabled.

Better breaking change notice

Hey folks,

I appreciate the Golang support but please documentation breaking API changes.

...GetConfig().GetArtDetails undefined (type config.Config has no field or method GetArtDetails)

This was broken upgrading from v0.8.1 -> v0.9.0

This commit broke the API and there were no documentation/announcement for it a265a82

This method is a public API so please dont just change it without announcement :(

Improve ContentReader Length() performance

Is your feature request related to a problem? Please describe.
In order to get ContentReader's length, it iterates over all of its items.

Describe the solution you'd like to see
A cheaper mechanism to get a ContentReader's length.

goroutine stack exceeds for GetUrl() method

HI, the call

rtDetails := auth.NewArtifactoryDetails()
rtDetails.SetUrl("https://my.artifactory-endpoint.com/artifactory/")
rtDetails.SetUser("admin")
rtDetails.SetPassword("mypasswd")
artconfig, err := artifactory.NewConfigBuilder().SetArtDetails(rtDetails).SetThreads(3).SetDryRun(false).Build()
if err != nil {
	panic(err)
}
cli, err := artifactory.New(&rtDetails, artconfig)
if err != nil {
	panic(err)
}
_, err = cli.Ping()
if err != nil {
	panic(err)
}
fmt.Println(cli.GetConfig().GetUrl())

causes a goroutine stack exceeds (infinite recursion I guess)

goroutine 1 [running]:
github.com/jfrog/jfrog-client-go/artifactory.(*artifactoryServicesConfig).GetUrl(0x9c94960, 0x0, 0x0)
	/home/susanna/go/pkg/mod/github.com/jfrog/[email protected]/artifactory/config.go:29 +0x3a fp=0x12400368 sp=0x12400364 pc=0x8548eaa
github.com/jfrog/jfrog-client-go/artifactory.(*artifactoryServicesConfig).GetUrl(0x9c94960, 0x0, 0x0)
	/home/susanna/go/pkg/mod/github.com/jfrog/[email protected]/artifactory/config.go:30 +0x21 fp=0x12400378 sp=0x12400368 pc=0x8548e91
github.com/jfrog/jfrog-client-go/artifactory.(*artifactoryServicesConfig).GetUrl(0x9c94960, 0x0, 0x0)
	/home/susanna/go/pkg/mod/github.com/jfrog/[email protected]/artifactory/config.go:30 +0x21 fp=0x12400388 sp=0x12400378 pc=0x8548e91
github.com/jfrog/jfrog-client-go/artifactory.(*artifactoryServicesConfig).GetUrl(0x9c94960, 0x0, 0x0)

I'm using v0.4.0.
Thanks

Different URI schema returned from api/storage

Describe the bug
When using the api/storage endpoint to retrieve information about an artifact (specifically its downloadURI)
The URI schema returned does not match the URI schema used to contact artifactory

To Reproduce
curl -u user:password https://<artifactory_address>/artifactory/api/storage/<repository_name>/<file_path>

response:
{
"repo" : "repository_name",
"path" : "file_path",
"created" : "2020-11-04T03:20:30.406-08:00",
"createdBy" : "user",
"lastModified" : "2020-11-04T03:20:29.193-08:00",
"modifiedBy" : "user",
"lastUpdated" : "2020-11-04T03:20:30.406-08:00",
"downloadUri" : "http://artifactory_address/artifactory/repository_name/file_path",
"mimeType" : "application/x-gzip",
"size" : "size",
"checksums" : {
"sha1" : "hash",
"md5" : "hash",
"sha256" : "hash"
},
"originalChecksums" : {
"sha256" : "hash"
},
"uri" : "http://artifactory_address/artifactory/api/storage/repository_name/file_path"
}

Expected behavior
When accessing artifactory using https, I expect the returned downloadURI to have an https schema.

Screenshots
If applicable, add screenshots to help explain your problem.

Versions

  • JFrog Go client version:
  • JFrog Go client operating system: macos,ubuntu
  • Artifactory version: 6.20.1

Additional context

I couldn't find where to open this issue, this might not be suitable since I'm not using the go sdk per se

Add RPM package

We use Artifactory throughout our organization, installing the cli on many servers running RHEL. Installing, deleting and updating the cli would be much simpler and less error-prone if done with a package manager.

Just as Artifactory server is available as an RPM distribution, the client should be as well.

Archive and explode multiple groups in spec

Is your feature request related to a problem? Please describe.
When using file specs to upload as archive, multiple groups can have the same target. Each of these groups might have a different value for the explode option. The value of the first of these groups to appear in the spec determines if the archive will be exploded or not. The current behavior is not what we'd expect the CLI to do.

Describe the solution you'd like to see
The groups with the archive flag set to true will be uploaded separately and be exploded, while the other groups will be uploaded in the same archive.

Describe alternatives you've considered
If the explode flag is true in at least one of the groups, the archive will be exploded. If, in addition, the flag is set explicitly to false in another group with the same target, then an error will be returned.
Another option is to return an error if the value of explode is not the same in all of the groups with the same target.

Build Environment Variable Include / Exclude Filters Missing

This library is missing the logic for build environment variable include / exclude patterns. It defines the configuration structure where the patterns are stored, but the filter logic is actually in the CLI code and is not exported so it is unusable.

type Configuration struct {
	ArtDetails auth.ServiceDetails
	BuildUrl   string
	DryRun     bool
	EnvInclude string
	EnvExclude string
}

From https://github.com/jfrog/jfrog-cli/blob/891920bdef88175224d15c77ea5850f580f96423/artifactory/commands/buildinfo/publish.go#L250:

type filterFunc func(map[string]string) (map[string]string, error)

func createIncludeFilter(pattern string) filterFunc {
	includePattern := strings.Split(pattern, ";")
	return func(tempMap map[string]string) (map[string]string, error) {
		result := make(map[string]string)
		for k, v := range tempMap {
			for _, filterPattern := range includePattern {
				matched, err := filepath.Match(strings.ToLower(filterPattern), strings.ToLower(strings.TrimPrefix(k, buildInfoPrefix)))
				if errorutils.CheckError(err) != nil {
					return nil, err
				}
				if matched {
					result[k] = v
					break
				}
			}
		}
		return result, nil
	}
}

func createExcludeFilter(pattern string) filterFunc {
	excludePattern := strings.Split(pattern, ";")
	return func(tempMap map[string]string) (map[string]string, error) {
		result := make(map[string]string)
		for k, v := range tempMap {
			include := true
			for _, filterPattern := range excludePattern {
				matched, err := filepath.Match(strings.ToLower(filterPattern), strings.ToLower(strings.TrimPrefix(k, buildInfoPrefix)))
				if errorutils.CheckError(err) != nil {
					return nil, err
				}
				if matched {
					include = false
					break
				}
			}
			if include {
				result[k] = v
			}
		}
		return result, nil
	}
}

Cannot get Ping() to work

I have a working jfrog-cli setup on my machine, which i want to reproduce in Go code.

$ jfrog rt ping
Ok

Trying to re-use this working setup in a Go program instead of the commandline, i waded through jfrog-cli-go, jfrog-go, and jfrog-client-go without any success. Documentation suggests to use the artifactory service manager from jfrog-client-go, but the only place i found that knows how to instantiate configs from $HOME/.jfrog/jfrog-cli.conf is in jfrog-cli-go, unfortunately the config in client and cli are not interchangeable.

Trying to manually copy struct members Url, User and Password from cli to client struct results in a sigsev.

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x60 pc=0x9501a2]

goroutine 1 [running]:
github.com/jfrog/jfrog-client-go/artifactory/services.(*PingService).Ping(0xc00012be78, 0x0, 0x0, 0x0, 0xc00012e4b0, 0x0)
        go/pkg/mod/github.com/jfrog/[email protected]/artifactory/services/ping.go:39 +0x42
github.com/jfrog/jfrog-client-go/artifactory.(*ArtifactoryServicesManager).Ping(0xc00012e4b0, 0xb9f740, 0xc0000c6640, 0x0, 0x0, 0xc00012e4b0)
        go/pkg/mod/github.com/jfrog/[email protected]/artifactory/manager.go:166 +0x79
main.main()
        work/jfrog-lib/main.go:49 +0x36f

What i am missing is a clear, minimal, concise, working example of a main.go that can Ping() the artifactory server. My trial and error path currently looks like this:

package main

// github.com/jfrog/jfrog-cli-go/
// "github.com/jfrog/jfrog-cli-go/jfrog-cli/commands/generic"
// "github.com/jfrog/jfrog-cli-go/jfrog-cli/artifactory/generic"

import (
        "fmt"

        "github.com/jfrog/jfrog-cli-go/jfrog-cli/utils/config"
        "github.com/jfrog/jfrog-client-go/artifactory"
        "github.com/jfrog/jfrog-client-go/artifactory/auth"
)

func main() {
        cs, err := config.GetAllArtifactoryConfigs()
        if err != nil {
                fmt.Errorf("error reading configuration: %v\n", err)
        }
        var configDetails *config.ArtifactoryDetails
        for _, c := range cs {
                if c.IsDefault {
                        fmt.Printf("default entry: %+v\n", c)
                        configDetails = c
                }
        }
        if !configDetails.IsDefault {
                fmt.Errorf("configuration does not contain default entry")
        }

        authDetails := auth.NewArtifactoryDetails()
        authDetails.SetUrl(configDetails.Url)
        authDetails.SetUser(configDetails.User)
        authDetails.SetPassword(configDetails.Password)

        // Ping
        // rtManager, err := artifactory.New(dc)
        // client := httpclient.New()
        // ps := services.NewPingService(&client)
        // ps.Ping()
        config, _ := artifactory.NewConfigBuilder().SetInsecureTls(true).Build()
        asm, err := artifactory.New(&authDetails, config)
        if err != nil {
                fmt.Errorf("error creating artifactory services manager: %v\n",
                        err)
        }
        asm.Ping()
        /*
                rtManager, err := artifactory.New(rtDetails, serviceConfig)

                params := services.NewSearchParams()
                // params.Pattern = "repo/"
                params.Recursive = true

                searchResult := rtManager.SearchFiles(params)
        */

        /* Using the jfrog-cli-go package does not find generic search command
        s := generic.NewSearchCommand()
        fmt.Printf("search: %+v\n", s)
        */
}

#299 broke interface for DownloadFiles

When #299 was implemented, it seems that the second variable returned is no long the number of expected files, it's now returning the number of failures. While that is completely reasonable, the interface and the function manager.DownloadFiles both expect something else.

DownloadFiles(params ...services.DownloadParams) (totalDownloaded, totalExpected int, err error)

Expected behavior
Either the interface needs to change to totalFailed or ArtifactoryServicesManagerImp.DownloadFiles needs to change to return totalDownloaded+totalFailed.

Versions

  • JFrog Go client version: v0.20.1

Update go dependencies

The current module dependencies are:

  [ ]  github.com/buger/jsonparser 0.0.0-20180910192245-6acdf747ae99 -> 1.0.0
  [ ]  github.com/mholt/archiver   2.1.0+incompatible                -> 3.1.1+incompatible
  [ ]  github.com/pkg/errors       0.8.0                             -> 0.9.1
  [ ]  github.com/spf13/viper      1.2.1                             -> 1.7.1
  [ ]  github.com/stretchr/testify 1.2.2                             -> 1.6.1
  [ ]  github.com/xanzy/ssh-agent  0.2.0                             -> 0.2.1
  [ ]  golang.org/x/crypto         0.0.0-20181001203147-e3636079e1a4 -> 0.0.0-20200728195943-123391ffb6de
  [ ]  gopkg.in/src-d/go-git.v4    4.7.0                             -> 4.13.1

For us, at least the old github.com/mholt/archiver dependency leads to problems, since most other projects use 3.x.x. The 2.1.0 version is from 2018.

Also gopkg.in/src-d/go-git.v4 is dead. It is encouraged to use the maintained fork: github.com/go-git/go-git/v5.

Support for "project" in file specs

Is your feature request related to a problem? Please describe.
File specs support filtering the searched artifacts by a build, by provising the build name and build number.
Following the introduction of projects in Artifactory, file specs should also support providing the project of the filteredbuild.

Describe the solution you'd like to see
Add a new optional "project" property to the file spec schema.

Describe alternatives you've considered
N/A

Additional context
N/A

No longer able to compile

Hi,
It seems that this package is no longer usable. It it still being maintained?

When I try to compile anything using it, I get

../../github.com/jfrog/jfrog-client-go/httpclient/client.go:252:10: undefined: archiver.MatchingFormat
../../github.com/jfrog/jfrog-client-go/httpclient/client.go:306:12: arch.Read undefined (type archiver.Archiver has no field or method Read)
../../github.com/jfrog/jfrog-client-go/httpclient/client.go:316:20: invalid method expression archiver.Zip.Open (needs pointer receiver: (*archiver.Zip).Open)
../../github.com/jfrog/jfrog-client-go/httpclient/client.go:316:20: archiver.Zip.Open undefined (type archiver.Zip has no method Open)
../../github.com/jfrog/jfrog-client-go/httpclient/client.go:476:10: undefined: archiver.MatchingFormat

This appears to be caused by one of the dependencies (https://github.com/mholt/archiver) having been recently rewritten.

Getting Panic "Logger not initialized. See API documentation."

I am getting panic with message "Logger not initialized. See API documentation." when calling SearchFiles method

artifactoryDetails := auth.NewArtifactoryDetails()
artifactoryDetails.SetUrl("myprivaterepo.com/artifactory")
serviceConfig, err := config.NewConfigBuilder().
	SetServiceDetails(artifactoryDetails).
	Build()

artifactoryManager, err := artifactory.New(&artifactoryDetails, serviceConfig)

params := services.NewSearchParams()
params.Pattern = "my_artifact"
params.Recursive = true
reader, err := artifactoryMgr.SearchFiles(params) // panic after this line

I don't need logging here and couldn't find anything mentioning logging on the docs.
what am I doing wrong?

Artifactory response: 405 Method Not Allowed

I'm trying to download a file from an Artifactory repository.

I'm using jfrog-client-go version v0.9.0

    const artifactoryUrl = "My Artifactory URL"

rtDetails := auth.NewArtifactoryDetails()
rtDetails.SetUrl(artifactoryUrl)

serviceConfig, err := config.NewConfigBuilder().
	SetArtDetails(rtDetails).
	SetDryRun(false).
	Build()
if err != nil {
	panic(err)
}

rtManager, err := artifactory.New(&rtDetails, serviceConfig)
if err != nil {
	panic(err)
}

params := services.NewDownloadParams()
params.Target = "/tmp/file.json"
params.Pattern = "path to my file.json"

params.Recursive = true
params.Regexp = false
params.IncludeDirs = false
params.Flat = true
params.Explode = false
params.Symlink = false
params.Retries = 5

_, _, err = rtManager.DownloadFiles(params)

The code rtManager.DownloadFiles(params) leads to do a SendPost() from the code client.go of jfrog-client-go that fails with:
Artifactory response: 405 Method Not Allowed

Note that if I do a "curl -X POST" with the URL of the file that I want to recover from artifactory then I get the same issue 405 while it works like a charm with a "curl -X GET"

Did I miss something in the params structure ?
Should it be SendGet() instead of SendPost() ?

Use runtime.GOMAXPROCS(0) instead of define an arbitrary value of 3 threads

Hello,

Here: https://github.com/jfrog/jfrog-client-go/blob/master/config/configbuilder.go#L9, you set an arbitrary value for threads. This value seems to be set without reflection and to ignore the actual capacities of the machine that will run the client.

Wouldn't it be better to set this default value to: runtime.GOMAXPROCS (0) ?
This will allow to use the maximum thread available for the running machine or the redefined maximum.

Thank you in advance.

Regards.

Is insecure_tls supported?

insecureTls is supported in here

type httpClientBuilder struct {
	certificatesDirPath string
	clientCertPath      string
	clientCertKeyPath   string
	insecureTls         bool
	ctx                 context.Context
}

ServiceDetails struct in here defines methods for settings client certificate, artifactory urls etc. but doesn't have a method for setting insecure tls.

Is there a reason for it? or just missed.

Context: I would like to use the ServiceDetails object to set the insecureTLS flag rather than using a http client builder.

Logger not initialized Error

Why jfrog-client is forcing to use log. I am getting the below error while using httpclient (github.com/jfrog/jfrog-client-go/http/httpclient) and httputils (github.com/jfrog/jfrog-client-go/utils/io/httputils).

Logger not initialized. See API documentation.
../github.com/jfrog/[email protected]/utils/log/logger.go:88 (0xdeb3ae)
	validateLogInit: panic("Logger not initialized. See API documentation.")
../github.com/jfrog/[email protected]/utils/log/logger.go:93 (0xdeb3e4)
	Debug: validateLogInit()
../github.com/jfrog/[email protected]/http/httpclient/client.go:88 (0xdf0a84)
	(*HttpClient).Send: log.Debug(fmt.Sprintf("Sending HTTP %s request to: %s", method, URL))
../github.com/jfrog/[email protected]/http/httpclient/client.go:51 (0xdef9d7)

Once I am using log ("github.com/jfrog/jfrog-client-go/utils/log") and setting the log like this
jfroglog.SetLogger(jfroglog.NewLogger(2, os.Stderr)), it works fine.

CreateTempDir()

We can set the environment variable $TMPDIR which will be retrieved by tempDirBase = os.TempDir(), later on we only check if the value is not an empty string. Setting $TMPDIR to a non-existant path will cause this to fail when we attempt to create temp dirs in https://github.com/jfrog/jfrog-client-go/blob/master/utils/io/fileutils/temp.go#L37

Should we not verify/create the path specified in tempDirBase before attempting to use it in ioutil.TempDir? I'm not sure if this is intentional or not, hence the question.

GetPathsToDelete returns wrong type for DeleteFiles

According to the readme I should be able to delete like this:

params := services.NewDeleteParams()
params.Aql = utils.Aql{
	ItemsFind: query,
}
params.Recursive = true

pathsToDelete, err := rtManager.GetPathsToDelete(params)
rtManager.DeleteFiles(deleteItems)

But instead I have to convert the type coming from GetPathsToDelete like so:

params := services.NewDeleteParams()
params.Aql = utils.Aql{
	ItemsFind: query,
}
params.Recursive = true

pathsToDelete, err := rtManager.GetPathsToDelete(params)
deleteItems := make([]services.DeleteItem, len(pathsToDelete))
for i, item := range pathsToDelete {
	deleteItems[i] = item
}
rtManager.DeleteFiles(deleteItems)

Library lacks Read methods

Hi,

Various parts of this library allow you to Create, Update, and Delete objects via the API. However, they do not allow you to Read the objects back. Examples of this would be the accesskeys and entitlements implementations. This severely limits this libraries use.

There are Show methods, but they make the odd choice (considering this is a library) to dump JSON to stdout instead of returning the object to the caller.

Without the Read methods, it is difficult for a user of the library to know if an object exists before trying to Create or Update it, etc, unless they re-implement chunks of the API to fetch them object themselves.

This issue should be considered a feature request to add the missing Read methods.

Thanks.

Tech Debt: XrayServicesManager & ArtifactoryServicesManager Initialization

It seems like both Xray manger and Artifactory manger initialization function may receive the auth.ServiceDetails twice.
Directly, and as part of the Config struct. I couldn't find a good reason for this design that could led to inconsistent behavior.
Please look on the attached usage of the Xray manager:

func (ds *xrayDetails) getXrayVersion() (string, error) {
	cd := auth.ServiceDetails(ds)
	serviceConfig, err := config.NewConfigBuilder(). // BTW this err is being ignored.
		SetServiceDetails(cd).
		SetCertificatesPath(cd.GetClientCertPath()).
		Build()
	sm, err := xray.New(&cd, serviceConfig)  // The auth.ServiceDetails is passed twice to this initialization func

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.