From 6b3dae604c9632c1f2abf1022ad04af26c887576 Mon Sep 17 00:00:00 2001 From: Eyal Ben Moshe Date: Mon, 28 Aug 2023 16:42:53 +0300 Subject: [PATCH 1/7] Update Frogbot workflows (#2166) --- .github/workflows/frogbot-scan-and-fix.yml | 8 ++++---- .github/workflows/frogbot-scan-pr.yml | 23 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/frogbot-scan-and-fix.yml b/.github/workflows/frogbot-scan-and-fix.yml index 5de072c16..875e275ca 100644 --- a/.github/workflows/frogbot-scan-and-fix.yml +++ b/.github/workflows/frogbot-scan-and-fix.yml @@ -15,10 +15,6 @@ jobs: # The repository scanning will be triggered periodically on the following branches. branch: [ "dev" ] steps: - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.branch }} - # Install prerequisites - name: Setup Go uses: actions/setup-go@v3 @@ -40,3 +36,7 @@ jobs: JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} JFROG_CLI_LOG_LEVEL: "DEBUG" + # [Mandatory] + # The name of the branch on which Frogbot will perform the scan + JF_GIT_BASE_BRANCH: ${{ matrix.branch }} + diff --git a/.github/workflows/frogbot-scan-pr.yml b/.github/workflows/frogbot-scan-pr.yml index 575d843e6..06147e3cd 100644 --- a/.github/workflows/frogbot-scan-pr.yml +++ b/.github/workflows/frogbot-scan-pr.yml @@ -12,10 +12,6 @@ jobs: # "frogbot" GitHub environment can approve the pull request to be scanned. environment: frogbot steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - # Install prerequisites - name: Setup Go uses: actions/setup-go@v3 @@ -36,3 +32,22 @@ jobs: # The GitHub token automatically generated for the job JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} JFROG_CLI_LOG_LEVEL: "DEBUG" + + # [Optional] + # Configure the SMTP server to enable Frogbot to send emails with detected secrets in pull request scans. + # SMTP server URL including should the relevant port: (Example: smtp.server.com:8080) + JF_SMTP_SERVER: ${{ secrets.JF_SMTP_SERVER }} + + # [Mandatory if JF_SMTP_SERVER is set] + # The username required for authenticating with the SMTP server. + JF_SMTP_USER: ${{ secrets.JF_SMTP_USER }} + + # [Mandatory if JF_SMTP_SERVER is set] + # The password associated with the username required for authentication with the SMTP server. + JF_SMTP_PASSWORD: ${{ secrets.JF_SMTP_PASSWORD }} + + # [Optional] + # List of comma separated email addresses to receive email notifications about secrets + # detected during pull request scanning. The notification is also sent to the email set + # in the committer git profile regardless of whether this variable is set or not. + JF_EMAIL_RECEIVERS: "eco-system@jfrog.com" From 3b424360a982194b259eadfa227889e5b7408aba Mon Sep 17 00:00:00 2001 From: Assaf Attias <49212512+attiasas@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:24:04 +0300 Subject: [PATCH 2/7] Upgrade client to 1.31.6 core to 2.41.3 (#2167) --- go.mod | 4 ++-- go.sum | 8 ++++---- inttestutils/buildinfo.go | 4 ++-- xray_test.go | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index dc83f3523..be492e4d6 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d github.com/jfrog/build-info-go v1.9.8 github.com/jfrog/gofrog v1.3.0 - github.com/jfrog/jfrog-cli-core/v2 v2.41.2 - github.com/jfrog/jfrog-client-go v1.31.5 + github.com/jfrog/jfrog-cli-core/v2 v2.41.3 + github.com/jfrog/jfrog-client-go v1.31.6 github.com/jszwec/csvutil v1.8.0 github.com/mholt/archiver/v3 v3.5.1 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index bd0657de8..676f79685 100644 --- a/go.sum +++ b/go.sum @@ -241,10 +241,10 @@ github.com/jfrog/build-info-go v1.9.8 h1:D8/ga+YgQpqp/CJj2zteS4/twmSy8zvm1v9lCd2 github.com/jfrog/build-info-go v1.9.8/go.mod h1:t31QRpH5xUJKw8XkQlAA+Aq7aanyS1rrzpcK8xSNVts= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.41.2 h1:Gnp93JcDAnHHCN3SHqam2K/S9yJcytS4q+MQd6vv9Ck= -github.com/jfrog/jfrog-cli-core/v2 v2.41.2/go.mod h1:YqB9rEJF1P7uGLIPUvF5qdDDf1zM5f4DneIQNkqyAfs= -github.com/jfrog/jfrog-client-go v1.31.5 h1:dYVgIJzMwX+EU9GEELKPSHFLyfW6UrrjZWMEZtAyx6A= -github.com/jfrog/jfrog-client-go v1.31.5/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= +github.com/jfrog/jfrog-cli-core/v2 v2.41.3 h1:BkPjDdrjO2jTlrI0/9Wps1lGNZmSwlIKBPJA+Wd99pE= +github.com/jfrog/jfrog-cli-core/v2 v2.41.3/go.mod h1:h3VQ3fxXU8dzPCINAFfzOWMwkV34/iBTgLnXAMrIc0Y= +github.com/jfrog/jfrog-client-go v1.31.6 h1:uWuyT4BDm9s5ES6oDTBny9Gl6yf8iKFjcbmHSHQZrDc= +github.com/jfrog/jfrog-client-go v1.31.6/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk= diff --git a/inttestutils/buildinfo.go b/inttestutils/buildinfo.go index 954ca6987..3003172bf 100644 --- a/inttestutils/buildinfo.go +++ b/inttestutils/buildinfo.go @@ -11,8 +11,8 @@ import ( coreutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "github.com/jfrog/jfrog-client-go/http/httpclient" + "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/io/httputils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/stretchr/testify/assert" @@ -27,7 +27,7 @@ func DeleteBuild(artifactoryUrl, buildName string, artHttpDetails httputils.Http restApi := path.Join("api/build/", buildName) params := map[string]string{"deleteAll": "1"} - requestFullUrl, err := utils.BuildArtifactoryUrl(artifactoryUrl, restApi, params) + requestFullUrl, err := utils.BuildUrl(artifactoryUrl, restApi, params) if err != nil { log.Error(err) return diff --git a/xray_test.go b/xray_test.go index 857c8da1f..d1638b483 100644 --- a/xray_test.go +++ b/xray_test.go @@ -485,7 +485,7 @@ func validateXrayVersion(t *testing.T, minVersion string) { assert.NoError(t, err) return } - err = coreutils.ValidateMinimumVersion(coreutils.Xray, xrayVersion.GetVersion(), minVersion) + err = clientUtils.ValidateMinimumVersion(clientUtils.Xray, xrayVersion.GetVersion(), minVersion) if err != nil { t.Skip(err) } From d71026b0e7eed5403ffd58801c3ee440ffca21c4 Mon Sep 17 00:00:00 2001 From: Yahav Itzhak Date: Mon, 28 Aug 2023 20:26:22 +0300 Subject: [PATCH 3/7] Support Gradle version catalog, configuration cache and lazy tasks (#2161) --- .github/workflows/gradleTests.yml | 5 +++-- go.mod | 8 ++++---- go.sum | 8 ++++---- testdata/gradle/projectwithplugin/build.gradle | 16 +++++++++++++++- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/.github/workflows/gradleTests.yml b/.github/workflows/gradleTests.yml index d361d9e9b..6feac8097 100644 --- a/.github/workflows/gradleTests.yml +++ b/.github/workflows/gradleTests.yml @@ -15,11 +15,12 @@ concurrency: jobs: Gradle-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: ${{ matrix.os }}-gradle-${{ matrix.gradle-version }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] + gradle-version: [5.6.4, 8.3] runs-on: ${{ matrix.os }} env: GRADLE_OPTS: -Dorg.gradle.daemon=false @@ -31,7 +32,7 @@ jobs: - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: ${{ matrix.gradle-version }} - name: Checkout code uses: actions/checkout@v3 with: diff --git a/go.mod b/go.mod index be492e4d6..225ba7f81 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,9 @@ require ( github.com/buger/jsonparser v1.1.1 github.com/go-git/go-git/v5 v5.8.1 github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d - github.com/jfrog/build-info-go v1.9.8 + github.com/jfrog/build-info-go v1.9.9 github.com/jfrog/gofrog v1.3.0 - github.com/jfrog/jfrog-cli-core/v2 v2.41.3 + github.com/jfrog/jfrog-cli-core/v2 v2.41.4 github.com/jfrog/jfrog-client-go v1.31.6 github.com/jszwec/csvutil v1.8.0 github.com/mholt/archiver/v3 v3.5.1 @@ -122,9 +122,9 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230820165857-52ff32c4d8eb +// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230828134416-f0db33dd9344 -// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/eyalbe4/jfrog-cli-core/v2 v2.22.1-0.20230825095403-f5869e4264d6 +// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230828140932-e44caa02288e // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 diff --git a/go.sum b/go.sum index 676f79685..18ca77ea6 100644 --- a/go.sum +++ b/go.sum @@ -237,12 +237,12 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw= github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= -github.com/jfrog/build-info-go v1.9.8 h1:D8/ga+YgQpqp/CJj2zteS4/twmSy8zvm1v9lCd2Kv1M= -github.com/jfrog/build-info-go v1.9.8/go.mod h1:t31QRpH5xUJKw8XkQlAA+Aq7aanyS1rrzpcK8xSNVts= +github.com/jfrog/build-info-go v1.9.9 h1:YMA9okHawBNL8SrCWzqULSf5M4W+YnWyUhmkWSjoXEE= +github.com/jfrog/build-info-go v1.9.9/go.mod h1:t31QRpH5xUJKw8XkQlAA+Aq7aanyS1rrzpcK8xSNVts= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.41.3 h1:BkPjDdrjO2jTlrI0/9Wps1lGNZmSwlIKBPJA+Wd99pE= -github.com/jfrog/jfrog-cli-core/v2 v2.41.3/go.mod h1:h3VQ3fxXU8dzPCINAFfzOWMwkV34/iBTgLnXAMrIc0Y= +github.com/jfrog/jfrog-cli-core/v2 v2.41.4 h1:+V35NN+UaKl6ZFSjAyZFZ4VijCgsORnGsHug02DROdE= +github.com/jfrog/jfrog-cli-core/v2 v2.41.4/go.mod h1:Mi3WFUzG2CU6tlLpGsMNRaKkhH/tIMuci4tjnPZ9S3M= github.com/jfrog/jfrog-client-go v1.31.6 h1:uWuyT4BDm9s5ES6oDTBny9Gl6yf8iKFjcbmHSHQZrDc= github.com/jfrog/jfrog-client-go v1.31.6/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= diff --git a/testdata/gradle/projectwithplugin/build.gradle b/testdata/gradle/projectwithplugin/build.gradle index 7664f11cb..d06c9fa99 100644 --- a/testdata/gradle/projectwithplugin/build.gradle +++ b/testdata/gradle/projectwithplugin/build.gradle @@ -9,6 +9,8 @@ buildscript { apply plugin: 'groovy' apply plugin: 'idea' apply plugin: 'com.jfrog.artifactory' +apply plugin: 'maven-publish' + version = 1.0 task initProject(description: 'Initialize project directory structure.') { doLast { @@ -28,6 +30,18 @@ task initProject(description: 'Initialize project directory structure.') { } } +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } +} + +tasks.withType(GenerateModuleMetadata) { + enabled = false +} + artifactory { contextUrl = "${URL}" publish { @@ -40,7 +54,7 @@ artifactory { // Reference to Gradle publications defined in the build script. // This is how we tell the Artifactory Plugin which artifacts should be // published to Artifactory. - publishConfigs('archives', 'published') + publications('mavenJava') publishArtifacts = true // Properties to be attached to the published artifacts. properties = ['qa.level': 'basic', 'dev.team' : 'core'] From c72d7d84b95b8f87bda98925e5efcb2310262311 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Wed, 30 Aug 2023 16:20:39 +0300 Subject: [PATCH 4/7] Support Lifecycle Release Bundle Distribute (#2068) --- .github/workflows/lifecycleTests.yml | 44 +++++++++++++++ distribution/cli.go | 76 +++++++++----------------- distribution_test.go | 3 +- docs/lifecycle/distribute/help.go | 15 ++++++ go.mod | 4 +- go.sum | 8 +-- lifecycle/cli.go | 58 +++++++++++++++++++- lifecycle_test.go | 43 ++++++++++----- utils/cliutils/commandsflags.go | 81 ++++++++++++++++++---------- utils/distribution/distribute.go | 52 ++++++++++++++++++ utils/tests/consts.go | 14 +++++ utils/tests/utils.go | 2 +- 12 files changed, 297 insertions(+), 103 deletions(-) create mode 100644 .github/workflows/lifecycleTests.yml create mode 100644 docs/lifecycle/distribute/help.go create mode 100644 utils/distribution/distribute.go diff --git a/.github/workflows/lifecycleTests.yml b/.github/workflows/lifecycleTests.yml new file mode 100644 index 000000000..01613e0af --- /dev/null +++ b/.github/workflows/lifecycleTests.yml @@ -0,0 +1,44 @@ +name: Lifecycle Tests +on: + push: + branches: + - '**' + tags-ignore: + - '**' + # Triggers the workflow on labeled PRs only. + pull_request_target: + types: [labeled] +# Ensures that only the latest commit is running for each PR at a time. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }} + cancel-in-progress: true +jobs: + Lifecycle-Tests: + if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' + name: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: 1.20.x + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Go Cache + uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Run Lifecycle tests + run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.lifecycle --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --jfrog.user=${{ secrets.PLATFORM_USER }} --ci.runId=${{ runner.os }}-lifecycle diff --git a/distribution/cli.go b/distribution/cli.go index 682156767..0a0664a29 100644 --- a/distribution/cli.go +++ b/distribution/cli.go @@ -2,10 +2,6 @@ package distribution import ( "errors" - "os" - "path/filepath" - "strings" - "github.com/jfrog/jfrog-cli-core/v2/common/commands" "github.com/jfrog/jfrog-cli-core/v2/common/spec" distributionCommands "github.com/jfrog/jfrog-cli-core/v2/distribution/commands" @@ -18,10 +14,14 @@ import ( "github.com/jfrog/jfrog-cli/docs/artifactory/releasebundleupdate" "github.com/jfrog/jfrog-cli/docs/common" "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-cli/utils/distribution" distributionServices "github.com/jfrog/jfrog-client-go/distribution/services" distributionServicesUtils "github.com/jfrog/jfrog-client-go/distribution/services/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/urfave/cli" + "os" + "path/filepath" + "strings" ) func GetCommands() []cli.Command { @@ -111,11 +111,11 @@ func releaseBundleCreateCmd(c *cli.Context) error { return err } releaseBundleCreateCmd := distributionCommands.NewReleaseBundleCreateCommand() - rtDetails, err := createArtifactoryDetailsByFlags(c) + dsDetails, err := createDistributionDetailsByFlags(c) if err != nil { return err } - releaseBundleCreateCmd.SetServerDetails(rtDetails).SetReleaseBundleCreateParams(params).SetSpec(releaseBundleCreateSpec).SetDryRun(c.Bool("dry-run")).SetDetailedSummary(c.Bool("detailed-summary")) + releaseBundleCreateCmd.SetServerDetails(dsDetails).SetReleaseBundleCreateParams(params).SetSpec(releaseBundleCreateSpec).SetDryRun(c.Bool("dry-run")).SetDetailedSummary(c.Bool("detailed-summary")) err = commands.Exec(releaseBundleCreateCmd) if releaseBundleCreateCmd.IsDetailedSummary() { @@ -153,11 +153,11 @@ func releaseBundleUpdateCmd(c *cli.Context) error { return err } releaseBundleUpdateCmd := distributionCommands.NewReleaseBundleUpdateCommand() - rtDetails, err := createArtifactoryDetailsByFlags(c) + dsDetails, err := createDistributionDetailsByFlags(c) if err != nil { return err } - releaseBundleUpdateCmd.SetServerDetails(rtDetails).SetReleaseBundleUpdateParams(params).SetSpec(releaseBundleUpdateSpec).SetDryRun(c.Bool("dry-run")).SetDetailedSummary(c.Bool("detailed-summary")) + releaseBundleUpdateCmd.SetServerDetails(dsDetails).SetReleaseBundleUpdateParams(params).SetSpec(releaseBundleUpdateSpec).SetDryRun(c.Bool("dry-run")).SetDetailedSummary(c.Bool("detailed-summary")) err = commands.Exec(releaseBundleUpdateCmd) if releaseBundleUpdateCmd.IsDetailedSummary() { @@ -177,11 +177,11 @@ func releaseBundleSignCmd(c *cli.Context) error { params.StoringRepository = c.String("repo") params.GpgPassphrase = c.String("passphrase") releaseBundleSignCmd := distributionCommands.NewReleaseBundleSignCommand() - rtDetails, err := createArtifactoryDetailsByFlags(c) + dsDetails, err := createDistributionDetailsByFlags(c) if err != nil { return err } - releaseBundleSignCmd.SetServerDetails(rtDetails).SetReleaseBundleSignParams(params).SetDetailedSummary(c.Bool("detailed-summary")) + releaseBundleSignCmd.SetServerDetails(dsDetails).SetReleaseBundleSignParams(params).SetDetailedSummary(c.Bool("detailed-summary")) err = commands.Exec(releaseBundleSignCmd) if releaseBundleSignCmd.IsDetailedSummary() { if summary := releaseBundleSignCmd.GetSummary(); summary != nil { @@ -192,37 +192,21 @@ func releaseBundleSignCmd(c *cli.Context) error { } func releaseBundleDistributeCmd(c *cli.Context) error { - if c.NArg() != 2 { - return cliutils.WrongNumberOfArgumentsHandler(c) - } - if c.IsSet("max-wait-minutes") && !c.IsSet("sync") { - return cliutils.PrintHelpAndReturnError("The --max-wait-minutes option can't be used without --sync", c) - } - var distributionRules *spec.DistributionRules - if c.IsSet("dist-rules") { - if c.IsSet("site") || c.IsSet("city") || c.IsSet("country-code") { - return cliutils.PrintHelpAndReturnError("The --dist-rules option can't be used with --site, --city or --country-code", c) - } - var err error - distributionRules, err = spec.CreateDistributionRulesFromFile(c.String("dist-rules")) - if err != nil { - return err - } - } else { - distributionRules = createDefaultDistributionRules(c) + if err := distribution.ValidateReleaseBundleDistributeCmd(c); err != nil { + return err } - params := distributionServices.NewDistributeReleaseBundleParams(c.Args().Get(0), c.Args().Get(1)) - releaseBundleDistributeCmd := distributionCommands.NewReleaseBundleDistributeCommand() - rtDetails, err := createArtifactoryDetailsByFlags(c) + dsDetails, err := createDistributionDetailsByFlags(c) if err != nil { return err } - maxWaitMinutes, err := cliutils.GetIntFlagValue(c, "max-wait-minutes", 60) + distributionRules, maxWaitMinutes, params, err := distribution.InitReleaseBundleDistributeCmd(c) if err != nil { return err } - releaseBundleDistributeCmd.SetServerDetails(rtDetails). + + distributeCmd := distributionCommands.NewReleaseBundleDistributeV1Command() + distributeCmd.SetServerDetails(dsDetails). SetDistributeBundleParams(params). SetDistributionRules(distributionRules). SetDryRun(c.Bool("dry-run")). @@ -230,7 +214,7 @@ func releaseBundleDistributeCmd(c *cli.Context) error { SetMaxWaitMinutes(maxWaitMinutes). SetAutoCreateRepo(c.Bool("create-repo")) - return commands.Exec(releaseBundleDistributeCmd) + return commands.Exec(distributeCmd) } func releaseBundleDeleteCmd(c *cli.Context) error { @@ -248,7 +232,7 @@ func releaseBundleDeleteCmd(c *cli.Context) error { return err } } else { - distributionRules = createDefaultDistributionRules(c) + distributionRules = distribution.CreateDefaultDistributionRules(c) } params := distributionServices.NewDeleteReleaseBundleParams(c.Args().Get(0), c.Args().Get(1)) @@ -260,11 +244,11 @@ func releaseBundleDeleteCmd(c *cli.Context) error { } params.MaxWaitMinutes = maxWaitMinutes distributeBundleCmd := distributionCommands.NewReleaseBundleDeleteParams() - rtDetails, err := createArtifactoryDetailsByFlags(c) + dsDetails, err := createDistributionDetailsByFlags(c) if err != nil { return err } - distributeBundleCmd.SetQuiet(cliutils.GetQuietValue(c)).SetServerDetails(rtDetails).SetDistributeBundleParams(params).SetDistributionRules(distributionRules).SetDryRun(c.Bool("dry-run")) + distributeBundleCmd.SetQuiet(cliutils.GetQuietValue(c)).SetServerDetails(dsDetails).SetDistributeBundleParams(params).SetDistributionRules(distributionRules).SetDryRun(c.Bool("dry-run")) return commands.Exec(distributeBundleCmd) } @@ -283,16 +267,6 @@ func createDefaultReleaseBundleSpec(c *cli.Context) *spec.SpecFiles { BuildSpec() } -func createDefaultDistributionRules(c *cli.Context) *spec.DistributionRules { - return &spec.DistributionRules{ - DistributionRules: []spec.DistributionRule{{ - SiteName: c.String("site"), - CityName: c.String("city"), - CountryCodes: cliutils.GetStringsArrFlagValue(c, "country-codes"), - }}, - } -} - func createReleaseBundleCreateUpdateParams(c *cli.Context, bundleName, bundleVersion string) (distributionServicesUtils.ReleaseBundleParams, error) { releaseBundleParams := distributionServicesUtils.NewReleaseBundleParams(bundleName, bundleVersion) releaseBundleParams.SignImmediately = c.Bool("sign") @@ -336,13 +310,13 @@ func populateReleaseNotesSyntax(c *cli.Context) (distributionServicesUtils.Relea return distributionServicesUtils.PlainText, nil } -func createArtifactoryDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { - artDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, cliutils.Ds) +func createDistributionDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { + dsDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, cliutils.Ds) if err != nil { return nil, err } - if artDetails.DistributionUrl == "" { + if dsDetails.DistributionUrl == "" { return nil, errors.New("the --dist-url option is mandatory") } - return artDetails, nil + return dsDetails, nil } diff --git a/distribution_test.go b/distribution_test.go index 9c23f7304..62cbea526 100644 --- a/distribution_test.go +++ b/distribution_test.go @@ -14,6 +14,7 @@ import ( distributionServices "github.com/jfrog/jfrog-client-go/distribution/services" clientDistUtils "github.com/jfrog/jfrog-client-go/distribution/services/utils" clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/distribution" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/io/httputils" "github.com/jfrog/jfrog-client-go/utils/log" @@ -566,7 +567,7 @@ func TestDistributeSyncTimeout(t *testing.T) { testServer, mockServerDetails, _ := coreTestUtils.CreateDsRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/api/v1/distribution/"+tests.BundleName+"/"+bundleVersion { w.WriteHeader(http.StatusOK) - content, err := json.Marshal(distributionServices.DistributionResponseBody{TrackerId: json.Number(trackerId)}) + content, err := json.Marshal(distribution.DistributionResponseBody{TrackerId: json.Number(trackerId)}) assert.NoError(t, err) _, err = w.Write(content) assert.NoError(t, err) diff --git a/docs/lifecycle/distribute/help.go b/docs/lifecycle/distribute/help.go new file mode 100644 index 000000000..8a3c2d47f --- /dev/null +++ b/docs/lifecycle/distribute/help.go @@ -0,0 +1,15 @@ +package distribute + +var Usage = []string{"rbd [command options] "} + +func GetDescription() string { + return "Distribute a release bundle." +} + +func GetArguments() string { + return ` release bundle name + Name of the Release Bundle to distribute. + + release bundle version + Version of the Release Bundle to distribute.` +} diff --git a/go.mod b/go.mod index 225ba7f81..737d29c7d 100644 --- a/go.mod +++ b/go.mod @@ -124,8 +124,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230828134416-f0db33dd9344 -// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230828140932-e44caa02288e +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230830130857-c5a2b11b52be // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230803140217-0a5f43783ae8 +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230830130057-df2d2a80b555 diff --git a/go.sum b/go.sum index 18ca77ea6..d00b21ab6 100644 --- a/go.sum +++ b/go.sum @@ -241,10 +241,10 @@ github.com/jfrog/build-info-go v1.9.9 h1:YMA9okHawBNL8SrCWzqULSf5M4W+YnWyUhmkWSj github.com/jfrog/build-info-go v1.9.9/go.mod h1:t31QRpH5xUJKw8XkQlAA+Aq7aanyS1rrzpcK8xSNVts= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.41.4 h1:+V35NN+UaKl6ZFSjAyZFZ4VijCgsORnGsHug02DROdE= -github.com/jfrog/jfrog-cli-core/v2 v2.41.4/go.mod h1:Mi3WFUzG2CU6tlLpGsMNRaKkhH/tIMuci4tjnPZ9S3M= -github.com/jfrog/jfrog-client-go v1.31.6 h1:uWuyT4BDm9s5ES6oDTBny9Gl6yf8iKFjcbmHSHQZrDc= -github.com/jfrog/jfrog-client-go v1.31.6/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230830130857-c5a2b11b52be h1:MjbSKQy937o0WFBKCXtvkX4EUSPCaA1LIhGISJUjYbU= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230830130857-c5a2b11b52be/go.mod h1:kaFzB3X83/jdzMcuGOYOaqnlz5MVTnDYt/asrOsCv18= +github.com/jfrog/jfrog-client-go v1.28.1-0.20230830130057-df2d2a80b555 h1:yaF5J4LNk+ws5+j+BFMJ43NMqpKuCtF7dUfCeGLttl8= +github.com/jfrog/jfrog-client-go v1.28.1-0.20230830130057-df2d2a80b555/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk= diff --git a/lifecycle/cli.go b/lifecycle/cli.go index 8caa332d2..647963c10 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -8,8 +8,10 @@ import ( coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli/docs/common" rbCreate "github.com/jfrog/jfrog-cli/docs/lifecycle/create" + rbDistribute "github.com/jfrog/jfrog-cli/docs/lifecycle/distribute" rbPromote "github.com/jfrog/jfrog-cli/docs/lifecycle/promote" "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-cli/utils/distribution" "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/urfave/cli" @@ -43,6 +45,19 @@ func GetCommands() []cli.Command { Category: lcCategory, Action: promote, }, + { + Name: "release-bundle-distribute", + Aliases: []string{"rbd"}, + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleDistribute), + Usage: rbDistribute.GetDescription(), + HelpName: coreCommon.CreateUsage("rbd", rbDistribute.GetDescription(), rbDistribute.Usage), + UsageText: rbDistribute.GetArguments(), + ArgsUsage: common.CreateEnvVars(), + BashComplete: coreCommon.CreateBashCompletionFunc(), + Category: lcCategory, + Hidden: true, + Action: distribute, + }, }) } @@ -77,7 +92,7 @@ func create(c *cli.Context) (err error) { return } - createCmd := lifecycle.NewReleaseBundleCreate().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). + createCmd := lifecycle.NewReleaseBundleCreateCommand().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.String(cliutils.SigningKey)).SetSync(c.Bool(cliutils.Sync)). SetReleaseBundleProject(cliutils.GetProject(c)).SetBuildsSpecPath(c.String(cliutils.Builds)). SetReleaseBundlesSpecPath(c.String(cliutils.ReleaseBundles)) @@ -102,12 +117,51 @@ func promote(c *cli.Context) error { return err } - createCmd := lifecycle.NewReleaseBundlePromote().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). + createCmd := lifecycle.NewReleaseBundlePromoteCommand().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). SetReleaseBundleVersion(c.Args().Get(1)).SetEnvironment(c.Args().Get(2)).SetSigningKeyName(c.String(cliutils.SigningKey)). SetSync(c.Bool(cliutils.Sync)).SetReleaseBundleProject(cliutils.GetProject(c)).SetOverwrite(c.Bool(cliutils.Overwrite)) return commands.Exec(createCmd) } +func distribute(c *cli.Context) error { + if err := validateDistributeCommand(c); err != nil { + return err + } + + lcDetails, err := createLifecycleDetailsByFlags(c) + if err != nil { + return err + } + distributionRules, _, params, err := distribution.InitReleaseBundleDistributeCmd(c) + if err != nil { + return err + } + + distributeCmd := lifecycle.NewReleaseBundleDistributeCommand() + distributeCmd.SetServerDetails(lcDetails). + SetDistributeBundleParams(params). + SetDistributionRules(distributionRules). + SetDryRun(c.Bool("dry-run")). + SetAutoCreateRepo(c.Bool(cliutils.CreateRepo)). + SetPathMappingPattern(c.String(cliutils.PathMappingPattern)). + SetPathMappingTarget(c.String(cliutils.PathMappingTarget)) + return commands.Exec(distributeCmd) +} + +func validateDistributeCommand(c *cli.Context) error { + if err := distribution.ValidateReleaseBundleDistributeCmd(c); err != nil { + return err + } + + mappingPatternProvided := c.IsSet(cliutils.PathMappingPattern) + mappingTargetProvided := c.IsSet(cliutils.PathMappingTarget) + if (mappingPatternProvided && !mappingTargetProvided) || + (!mappingPatternProvided && mappingTargetProvided) { + return errorutils.CheckErrorf("the options --%s and --%s must be provided together", cliutils.PathMappingPattern, cliutils.PathMappingTarget) + } + return nil +} + func assertSigningKeyProvided(c *cli.Context) error { if c.String(cliutils.SigningKey) == "" { return errorutils.CheckErrorf("the --%s option is mandatory", cliutils.SigningKey) diff --git a/lifecycle_test.go b/lifecycle_test.go index 24980c734..c81e2beba 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -23,14 +23,14 @@ import ( ) const ( - rbMinVersion = "7.45.0" - gpgKeyPairName = "lc-tests-key-pair" - lcTestdataPath = "lifecycle" - releaseBundlesSpec = "release-bundles-spec.json" - buildsSpec12 = "builds-spec-1-2.json" - buildsSpec3 = "builds-spec-3.json" - prodEnvironment = "PROD" - number1, number2, number3 = "111", "222", "333" + artifactoryLifecycleMinVersion = "7.65.0" + gpgKeyPairName = "lc-tests-key-pair" + lcTestdataPath = "lifecycle" + releaseBundlesSpec = "release-bundles-spec.json" + buildsSpec12 = "builds-spec-1-2.json" + buildsSpec3 = "builds-spec-3.json" + prodEnvironment = "PROD" + number1, number2, number3 = "111", "222", "333" ) var ( @@ -47,11 +47,11 @@ func TestLifecycle(t *testing.T) { deleteBuilds := uploadBuilds(t) defer deleteBuilds() - // Create release bundles from builds synchronously. + // Create release bundle from builds synchronously. createRb(t, buildsSpec12, cliutils.Builds, tests.LcRbName1, number1, true) defer deleteReleaseBundle(t, lcManager, tests.LcRbName1, number1) - // Create release bundles from builds asynchronously and assert status. + // Create release bundle from builds asynchronously and assert status. createRb(t, buildsSpec3, cliutils.Builds, tests.LcRbName2, number2, false) defer deleteReleaseBundle(t, lcManager, tests.LcRbName2, number2) assertStatusCompleted(t, lcManager, tests.LcRbName2, number2, "") @@ -67,6 +67,11 @@ func TestLifecycle(t *testing.T) { searchSpec, err := tests.CreateSpec(tests.SearchAllProdRepo) assert.NoError(t, err) inttestutils.VerifyExistInArtifactory(tests.GetExpectedLifecycleArtifacts(), searchSpec, serverDetails, t) + + distributeRb(t) + // Verify the artifacts were distributed correctly by the provided path mappings. + expected := append(tests.GetExpectedLifecycleArtifacts(), tests.GetExpectedLifecycleMappingArtifacts()...) + inttestutils.VerifyExistInArtifactory(expected, searchSpec, serverDetails, t) } func uploadBuilds(t *testing.T) func() { @@ -80,13 +85,13 @@ func uploadBuilds(t *testing.T) func() { } } -func createRb(t *testing.T, specName, sourceOption, buildName, buildNumber string, sync bool) { +func createRb(t *testing.T, specName, sourceOption, rbName, rbVersion string, sync bool) { specFile, err := getSpecFile(specName) assert.NoError(t, err) argsAndOptions := []string{ "rbc", - buildName, - buildNumber, + rbName, + rbVersion, getOption(sourceOption, specFile), getOption(cliutils.SigningKey, gpgKeyPairName), } @@ -97,6 +102,16 @@ func createRb(t *testing.T, specName, sourceOption, buildName, buildNumber strin assert.NoError(t, lcCli.Exec(argsAndOptions...)) } +func distributeRb(t *testing.T) { + distributionRulesPath := filepath.Join(tests.GetTestResourcesPath(), "distribution", tests.DistributionRules) + assert.NoError(t, lcCli.Exec( + "rbd", tests.LcRbName3, number3, + getOption(cliutils.DistRules, distributionRulesPath), + getOption(cliutils.PathMappingPattern, tests.RtProdRepo+"/(*)"), + getOption(cliutils.PathMappingTarget, tests.RtProdRepo+"/target/{1}"), + )) +} + func getOption(option, value string) string { return fmt.Sprintf("--%s=%s", option, value) } @@ -183,7 +198,7 @@ func initLifecycleTest(t *testing.T) { if !*tests.TestLifecycle { t.Skip("Skipping lifecycle test. To run release bundle test add the '-test.lc=true' option.") } - validateArtifactoryVersion(t, rbMinVersion) + validateArtifactoryVersion(t, artifactoryLifecycleMinVersion) if !isLifecycleSupported(t) { t.Skip("Skipping lifecycle test because the functionality is not enabled on the provided JPD.") diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 625841bc1..24bc36d46 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -131,8 +131,9 @@ const ( TransferInstall = "transfer-plugin-install" // Lifecycle commands keys - ReleaseBundleCreate = "release-bundle-create" - ReleaseBundlePromote = "release-bundle-promote" + ReleaseBundleCreate = "release-bundle-create" + ReleaseBundlePromote = "release-bundle-promote" + ReleaseBundleDistribute = "release-bundle-distribute" // *** Artifactory Commands' flags *** // Base flags @@ -424,14 +425,16 @@ const ( desc = "desc" releaseNotesPath = "release-notes-path" releaseNotesSyntax = "release-notes-syntax" - distRules = "dist-rules" - site = "site" - city = "city" - countryCodes = "country-codes" - sync = "sync" - maxWaitMinutes = "max-wait-minutes" deleteFromDist = "delete-from-dist" - createRepo = "create-repo" + + // Common release-bundle-* v1&v2 flags + DistRules = "dist-rules" + site = "site" + city = "city" + countryCodes = "country-codes" + sync = "sync" + maxWaitMinutes = "max-wait-minutes" + CreateRepo = "create-repo" // *** Xray Commands' flags *** // Base flags @@ -542,17 +545,22 @@ const ( InstallPluginHomeDir = "home-dir" // Unique lifecycle flags - lifecyclePrefix = "lc-" - lcUrl = lifecyclePrefix + url - lcSync = lifecyclePrefix + Sync - lcProject = lifecyclePrefix + project - Builds = "builds" - lcBuilds = lifecyclePrefix + Builds - ReleaseBundles = "release-bundles" - lcReleaseBundles = lifecyclePrefix + ReleaseBundles - SigningKey = "signing-key" - lcSigningKey = lifecyclePrefix + SigningKey - lcOverwrite = lifecyclePrefix + Overwrite + lifecyclePrefix = "lc-" + lcUrl = lifecyclePrefix + url + lcSync = lifecyclePrefix + Sync + lcProject = lifecyclePrefix + project + Builds = "builds" + lcBuilds = lifecyclePrefix + Builds + ReleaseBundles = "release-bundles" + lcReleaseBundles = lifecyclePrefix + ReleaseBundles + SigningKey = "signing-key" + lcSigningKey = lifecyclePrefix + SigningKey + lcOverwrite = lifecyclePrefix + Overwrite + PathMappingPattern = "mapping-pattern" + lcPathMappingPattern = lifecyclePrefix + PathMappingPattern + PathMappingTarget = "mapping-target" + lcPathMappingTarget = lifecyclePrefix + PathMappingTarget + lcDryRun = lifecyclePrefix + dryRun ) var flagsMap = map[string]cli.Flag{ @@ -1237,9 +1245,9 @@ var flagsMap = map[string]cli.Flag{ Name: repo, Usage: "[Optional] A repository name at source Artifactory to store release bundle artifacts in. If not provided, Artifactory will use the default one.` `", }, - distRules: cli.StringFlag{ - Name: distRules, - Usage: "Path to distribution rules.` `", + DistRules: cli.StringFlag{ + Name: DistRules, + Usage: "[Optional] Path to distribution rules.` `", }, site: cli.StringFlag{ Name: site, @@ -1509,8 +1517,8 @@ var flagsMap = map[string]cli.Flag{ Name: "format", Hidden: true, }, - createRepo: cli.BoolFlag{ - Name: createRepo, + CreateRepo: cli.BoolFlag{ + Name: CreateRepo, Usage: "[Default: false] Set to true to create the repository on the edge if it does not exist.` `", }, Filestore: cli.BoolFlag{ @@ -1613,6 +1621,19 @@ var flagsMap = map[string]cli.Flag{ Name: Overwrite, Usage: "[Default: false] Set to true to replace artifacts with the same name but a different checksum if such already exist at the promotion targets. By default, the promotion is stopped in a case of such conflict.` `", }, + lcPathMappingPattern: cli.StringFlag{ + Name: PathMappingPattern, + Usage: "[Optional] Specify along with '" + PathMappingTarget + "' to distribute artifacts to a different path on the edge node. You can use wildcards to specify multiple artifacts.` `", + }, + lcPathMappingTarget: cli.StringFlag{ + Name: PathMappingTarget, + Usage: "[Optional] The target path for distributed artifacts on the edge node. If not specified, the artifacts will have the same path and name on the edge node, as on the source Artifactory server. " + + "For flexibility in specifying the distribution path, you can include placeholders in the form of {1}, {2} which are replaced by corresponding tokens in the pattern path that are enclosed in parenthesis.` `", + }, + lcDryRun: cli.BoolFlag{ + Name: dryRun, + Usage: "[Default: false] Set to true to only simulate the distribution of the release bundle.` `", + }, } var commandFlags = map[string][]string{ @@ -1836,11 +1857,11 @@ var commandFlags = map[string][]string{ InsecureTls, rbDetailedSummary, }, ReleaseBundleV1Distribute: { - distUrl, user, password, accessToken, serverId, rbDryRun, distRules, - site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, createRepo, + distUrl, user, password, accessToken, serverId, rbDryRun, DistRules, + site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, CreateRepo, }, ReleaseBundleV1Delete: { - distUrl, user, password, accessToken, serverId, rbDryRun, distRules, + distUrl, user, password, accessToken, serverId, rbDryRun, DistRules, site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, deleteFromDist, deleteQuiet, }, TemplateConsumer: { @@ -1897,6 +1918,10 @@ var commandFlags = map[string][]string{ ReleaseBundlePromote: { lcUrl, user, password, accessToken, serverId, lcSigningKey, lcSync, lcProject, lcOverwrite, }, + ReleaseBundleDistribute: { + lcUrl, user, password, accessToken, serverId, lcDryRun, DistRules, site, city, countryCodes, + InsecureTls, CreateRepo, lcPathMappingPattern, lcPathMappingTarget, + }, // Xray's commands OfflineUpdate: { licenseId, from, to, Version, target, Stream, Periodic, diff --git a/utils/distribution/distribute.go b/utils/distribution/distribute.go new file mode 100644 index 000000000..bbc82bae9 --- /dev/null +++ b/utils/distribution/distribute.go @@ -0,0 +1,52 @@ +package distribution + +import ( + "github.com/jfrog/jfrog-cli-core/v2/common/spec" + "github.com/jfrog/jfrog-cli/utils/cliutils" + distributionUtils "github.com/jfrog/jfrog-client-go/utils/distribution" + "github.com/urfave/cli" +) + +func CreateDefaultDistributionRules(c *cli.Context) *spec.DistributionRules { + return &spec.DistributionRules{ + DistributionRules: []spec.DistributionRule{{ + SiteName: c.String("site"), + CityName: c.String("city"), + CountryCodes: cliutils.GetStringsArrFlagValue(c, "country-codes"), + }}, + } +} + +func ValidateReleaseBundleDistributeCmd(c *cli.Context) error { + if c.NArg() != 2 { + return cliutils.WrongNumberOfArgumentsHandler(c) + } + if c.IsSet("max-wait-minutes") && !c.IsSet("sync") { + return cliutils.PrintHelpAndReturnError("The --max-wait-minutes option can't be used without --sync", c) + } + + if c.IsSet("dist-rules") && (c.IsSet("site") || c.IsSet("city") || c.IsSet("country-code")) { + return cliutils.PrintHelpAndReturnError("The --dist-rules option can't be used with --site, --city or --country-code", c) + } + + return nil +} + +func InitReleaseBundleDistributeCmd(c *cli.Context) (distributionRules *spec.DistributionRules, maxWaitMinutes int, params distributionUtils.DistributionParams, err error) { + if c.IsSet("dist-rules") { + distributionRules, err = spec.CreateDistributionRulesFromFile(c.String("dist-rules")) + if err != nil { + return + } + } else { + distributionRules = CreateDefaultDistributionRules(c) + } + + maxWaitMinutes, err = cliutils.GetIntFlagValue(c, "max-wait-minutes", 60) + if err != nil { + return + } + + params = distributionUtils.NewDistributeReleaseBundleParams(c.Args().Get(0), c.Args().Get(1)) + return +} diff --git a/utils/tests/consts.go b/utils/tests/consts.go index 8373be6d7..f415aa1f0 100644 --- a/utils/tests/consts.go +++ b/utils/tests/consts.go @@ -2099,6 +2099,20 @@ func GetExpectedLifecycleArtifacts() []string { } } +func GetExpectedLifecycleMappingArtifacts() []string { + return []string{ + RtProdRepo + "/target/a1.in", + RtProdRepo + "/target/a2.in", + RtProdRepo + "/target/a3.in", + RtProdRepo + "/target/b1.in", + RtProdRepo + "/target/b2.in", + RtProdRepo + "/target/b3.in", + RtProdRepo + "/target/c1.in", + RtProdRepo + "/target/c2.in", + RtProdRepo + "/target/c3.in", + } +} + func GetGoPublishWithExclusionsExpectedRepoGo() []string { var expected = []string{ GoRepo + "/github.com/jfrog/dependency/@v/v1.1.1.info", diff --git a/utils/tests/utils.go b/utils/tests/utils.go index d5ab82381..19540b144 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -106,7 +106,7 @@ func init() { TestXray = flag.Bool("test.xray", false, "Test Xray") TestAccess = flag.Bool("test.access", false, "Test Access") TestTransfer = flag.Bool("test.transfer", false, "Test files transfer") - TestLifecycle = flag.Bool("test.lc", false, "Test lifecycle") + TestLifecycle = flag.Bool("test.lifecycle", false, "Test lifecycle") ContainerRegistry = flag.String("test.containerRegistry", "localhost:8082", "Container registry") HideUnitTestLog = flag.Bool("test.hideUnitTestLog", false, "Hide unit tests logs and print it in a file") InstallDataTransferPlugin = flag.Bool("test.installDataTransferPlugin", false, "Install data-transfer plugin on the source Artifactory server") From bd082a1b70efa5163998eb5c665aca2b0e5d67fe Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Wed, 30 Aug 2023 16:41:53 +0300 Subject: [PATCH 5/7] Skip lifecycle tests (#2173) --- lifecycle_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifecycle_test.go b/lifecycle_test.go index c81e2beba..5a1516df8 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -23,7 +23,7 @@ import ( ) const ( - artifactoryLifecycleMinVersion = "7.65.0" + artifactoryLifecycleMinVersion = "7.68.3" gpgKeyPairName = "lc-tests-key-pair" lcTestdataPath = "lifecycle" releaseBundlesSpec = "release-bundles-spec.json" From 5edd3c9fd668ead62f35aef4a9d2f823246bfe36 Mon Sep 17 00:00:00 2001 From: Michael Sverdlov Date: Thu, 31 Aug 2023 18:50:11 +0300 Subject: [PATCH 6/7] Fix Xray Gradle and Analyzer tests (#2174) --- .github/workflows/accessTests.yml | 6 +- .github/workflows/analysis.yml | 4 +- .github/workflows/artifactoryTests.yml | 4 +- .github/workflows/distributionTests.yml | 6 +- .github/workflows/frogbot-scan-and-fix.yml | 42 ---- .github/workflows/frogbot-scan-pr.yml | 53 ----- .../workflows/frogbot-scan-pull-request.yml | 119 +++++++++++ .github/workflows/frogbot-scan-repository.yml | 125 ++++++++++++ .github/workflows/goTests.yml | 6 +- .github/workflows/gradleTests.yml | 4 +- .github/workflows/lifecycleTests.yml | 6 +- .github/workflows/mavenTests.yml | 6 +- .github/workflows/npmTests.yml | 6 +- .github/workflows/nugetTests.yml | 6 +- .github/workflows/pluginsTests.yml | 6 +- .github/workflows/pythonTests.yml | 4 +- .github/workflows/scriptTests.yml | 6 +- .github/workflows/transferTests.yml | 6 +- .github/workflows/xrayTests.yml | 6 +- artifactory_test.go | 21 +- docker_test.go | 3 +- go.mod | 23 ++- go.sum | 42 ++-- go_test.go | 6 +- main_test.go | 6 +- maven_test.go | 4 +- npm_test.go | 13 +- nuget_test.go | 3 +- pip_test.go | 3 +- pipenv_test.go | 3 +- plugins/commands/uninstall_test.go | 3 +- plugins_test.go | 3 +- poetry_test.go | 3 +- .../dependency/createGoProject_go.mod_suffix | 0 .../go/project1/createGoProject_go.mod_suffix | 0 .../go/project2/createGoProject_go.mod_suffix | 0 .../go/project3/createGoProject_go.mod_suffix | 0 .../go/project4/createGoProject_go.mod_suffix | 0 .../createGoProject_go.mod_suffix | 0 .../createGoProject_go.mod_suffix | 0 .../vcsfallback/createGoProject_go.mod_suffix | 0 testdata/npm/npmnpmrcproject/.npmrc | 0 testdata/npm/npmnpmrcproject/package.json | 0 testdata/npm/npmpostinstall/package.json | 0 .../npm/npmpostinstall/subdir/package.json | 0 testdata/npm/npmproject/package.json | 0 testdata/npm/npmprojectci/package.json | 0 testdata/npm/npmscopedproject/package.json | 0 testdata/xray/gradle/api/build.gradle | 15 ++ .../main/java/org/gradle/api/PersonList.java | 38 ++++ .../src/main/java/org/gradle/api/package.html | 19 ++ .../main/java/org/gradle/apiImpl/Impl.java | 26 +++ testdata/xray/gradle/build.gradle | 48 +++-- .../gradle/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55616 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + testdata/xray/gradle/gradlew | 188 ++++++++++++++++++ testdata/xray/gradle/gradlew.bat | 100 ++++++++++ .../gradle/services/webservice/build.gradle | 8 + .../java/org/gradle/webservice/TestTest.java | 35 ++++ .../org/gradle/webservice/TestTestTest.java | 31 +++ testdata/xray/gradle/settings.gradle | 2 +- .../main/java/org/gradle/shared/Person.java | 42 ++++ .../java/org/gradle/shared/package-info.java | 20 ++ .../org/gradle/shared/main.properties | 16 ++ .../{jas => jas-test}/iac/azure/vpc/module.tf | 0 .../iac/azure/vpc/outputs.tf | 0 .../iac/azure/vpc/variables.tf | 0 .../iac/azure/vpc/versions.tf | 0 .../iac/azure/vpc_pp/module.tf | 0 .../iac/azure/vpc_pp/outputs.tf | 0 .../iac/azure/vpc_pp/variables.tf | 0 .../iac/azure/vpc_pp/versions.tf | 0 .../iac/gcp/k8s-oss/files/chk_k8s_nat | 0 .../iac/gcp/k8s-oss/module.tf | 0 .../iac/gcp/k8s-oss/outputs.tf | 0 .../iac/gcp/k8s-oss/variables.tf | 0 .../iac/gcp/k8s-oss/versions.tf | 0 .../gcp/k8s-pipelines-bp/files/chk_k8s_nat | 0 .../iac/gcp/k8s-pipelines-bp/module.tf | 0 .../iac/gcp/k8s-pipelines-bp/outputs.tf | 0 .../iac/gcp/k8s-pipelines-bp/rbac.tf | 0 .../iac/gcp/k8s-pipelines-bp/variables.tf | 0 .../iac/gcp/k8s-pipelines-bp/versions.tf | 0 testdata/xray/{jas => jas-test}/main.py | 0 .../xray/{jas => jas-test}/requirements.txt | 0 .../secrets/more_secrets/key | 0 .../secrets/more_secrets/sequence | 0 .../secrets/secret_generic/blacklist | 0 .../secrets/secret_generic/gibberish | 0 transfer_test.go | 3 +- utils/cliutils/utils_test.go | 4 +- xray_test.go | 33 +-- 92 files changed, 953 insertions(+), 237 deletions(-) delete mode 100644 .github/workflows/frogbot-scan-and-fix.yml delete mode 100644 .github/workflows/frogbot-scan-pr.yml create mode 100644 .github/workflows/frogbot-scan-pull-request.yml create mode 100644 .github/workflows/frogbot-scan-repository.yml mode change 100644 => 100755 testdata/go/dependency/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/project1/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/project2/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/project3/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/project4/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/projectbuild/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/projectmissingdependency/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/go/vcsfallback/createGoProject_go.mod_suffix mode change 100644 => 100755 testdata/npm/npmnpmrcproject/.npmrc mode change 100644 => 100755 testdata/npm/npmnpmrcproject/package.json mode change 100644 => 100755 testdata/npm/npmpostinstall/package.json mode change 100644 => 100755 testdata/npm/npmpostinstall/subdir/package.json mode change 100644 => 100755 testdata/npm/npmproject/package.json mode change 100644 => 100755 testdata/npm/npmprojectci/package.json mode change 100644 => 100755 testdata/npm/npmscopedproject/package.json create mode 100644 testdata/xray/gradle/api/build.gradle create mode 100644 testdata/xray/gradle/api/src/main/java/org/gradle/api/PersonList.java create mode 100644 testdata/xray/gradle/api/src/main/java/org/gradle/api/package.html create mode 100644 testdata/xray/gradle/api/src/main/java/org/gradle/apiImpl/Impl.java create mode 100755 testdata/xray/gradle/gradle/wrapper/gradle-wrapper.jar create mode 100755 testdata/xray/gradle/gradle/wrapper/gradle-wrapper.properties create mode 100755 testdata/xray/gradle/gradlew create mode 100755 testdata/xray/gradle/gradlew.bat create mode 100644 testdata/xray/gradle/services/webservice/build.gradle create mode 100644 testdata/xray/gradle/services/webservice/src/main/java/org/gradle/webservice/TestTest.java create mode 100644 testdata/xray/gradle/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java create mode 100644 testdata/xray/gradle/shared/src/main/java/org/gradle/shared/Person.java create mode 100644 testdata/xray/gradle/shared/src/main/java/org/gradle/shared/package-info.java create mode 100644 testdata/xray/gradle/shared/src/main/resources/org/gradle/shared/main.properties rename testdata/xray/{jas => jas-test}/iac/azure/vpc/module.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc/outputs.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc/variables.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc/versions.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc_pp/module.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc_pp/outputs.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc_pp/variables.tf (100%) rename testdata/xray/{jas => jas-test}/iac/azure/vpc_pp/versions.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-oss/files/chk_k8s_nat (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-oss/module.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-oss/outputs.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-oss/variables.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-oss/versions.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-pipelines-bp/module.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-pipelines-bp/outputs.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-pipelines-bp/rbac.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-pipelines-bp/variables.tf (100%) rename testdata/xray/{jas => jas-test}/iac/gcp/k8s-pipelines-bp/versions.tf (100%) rename testdata/xray/{jas => jas-test}/main.py (100%) rename testdata/xray/{jas => jas-test}/requirements.txt (100%) rename testdata/xray/{jas => jas-test}/secrets/more_secrets/key (100%) rename testdata/xray/{jas => jas-test}/secrets/more_secrets/sequence (100%) rename testdata/xray/{jas => jas-test}/secrets/secret_generic/blacklist (100%) rename testdata/xray/{jas => jas-test}/secrets/secret_generic/gibberish (100%) diff --git a/.github/workflows/accessTests.yml b/.github/workflows/accessTests.yml index 180e44521..f1f9d450f 100644 --- a/.github/workflows/accessTests.yml +++ b/.github/workflows/accessTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: Access-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Access tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index d3957b0d4..e78785890 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -15,8 +15,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/artifactoryTests.yml b/.github/workflows/artifactoryTests.yml index f87493209..440323b26 100644 --- a/.github/workflows/artifactoryTests.yml +++ b/.github/workflows/artifactoryTests.yml @@ -20,8 +20,8 @@ jobs: fail-fast: false matrix: suite: [ artifactory, artifactoryProject ] - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/distributionTests.yml b/.github/workflows/distributionTests.yml index c3fb0200a..7804ad677 100644 --- a/.github/workflows/distributionTests.yml +++ b/.github/workflows/distributionTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: Distribution-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Distribution tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/frogbot-scan-and-fix.yml b/.github/workflows/frogbot-scan-and-fix.yml deleted file mode 100644 index 875e275ca..000000000 --- a/.github/workflows/frogbot-scan-and-fix.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: "Frogbot Scan and Fix" -on: - schedule: - # The repository will be scanned once a day at 00:00 GMT. - - cron: "0 0 * * *" -permissions: - contents: write - pull-requests: write - security-events: write -jobs: - create-fix-pull-requests: - runs-on: ubuntu-latest - strategy: - matrix: - # The repository scanning will be triggered periodically on the following branches. - branch: [ "dev" ] - steps: - # Install prerequisites - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.20.x - - - uses: jfrog/frogbot@v2 - env: - # [Mandatory] - # JFrog platform URL - JF_URL: ${{ secrets.FROGBOT_URL }} - - # [Mandatory if JF_USER and JF_PASSWORD are not provided] - # JFrog access token with 'read' permissions on Xray service - JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} - - # [Mandatory] - # The GitHub token automatically generated for the job - JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JFROG_CLI_LOG_LEVEL: "DEBUG" - - # [Mandatory] - # The name of the branch on which Frogbot will perform the scan - JF_GIT_BASE_BRANCH: ${{ matrix.branch }} - diff --git a/.github/workflows/frogbot-scan-pr.yml b/.github/workflows/frogbot-scan-pr.yml deleted file mode 100644 index 06147e3cd..000000000 --- a/.github/workflows/frogbot-scan-pr.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: "Frogbot Scan Pull Request" -on: - pull_request_target: - types: [opened, synchronize] -permissions: - pull-requests: write - contents: read -jobs: - scan-pull-request: - runs-on: ubuntu-latest - # A pull request needs to be approved, before Frogbot scans it. Any GitHub user who is associated with the - # "frogbot" GitHub environment can approve the pull request to be scanned. - environment: frogbot - steps: - # Install prerequisites - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.20.x - - - uses: jfrog/frogbot@v2 - env: - # [Mandatory] - # JFrog platform URL - JF_URL: ${{ secrets.FROGBOT_URL }} - - # [Mandatory if JF_USER and JF_PASSWORD are not provided] - # JFrog access token with 'read' permissions on Xray service - JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} - - # [Mandatory] - # The GitHub token automatically generated for the job - JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - JFROG_CLI_LOG_LEVEL: "DEBUG" - - # [Optional] - # Configure the SMTP server to enable Frogbot to send emails with detected secrets in pull request scans. - # SMTP server URL including should the relevant port: (Example: smtp.server.com:8080) - JF_SMTP_SERVER: ${{ secrets.JF_SMTP_SERVER }} - - # [Mandatory if JF_SMTP_SERVER is set] - # The username required for authenticating with the SMTP server. - JF_SMTP_USER: ${{ secrets.JF_SMTP_USER }} - - # [Mandatory if JF_SMTP_SERVER is set] - # The password associated with the username required for authentication with the SMTP server. - JF_SMTP_PASSWORD: ${{ secrets.JF_SMTP_PASSWORD }} - - # [Optional] - # List of comma separated email addresses to receive email notifications about secrets - # detected during pull request scanning. The notification is also sent to the email set - # in the committer git profile regardless of whether this variable is set or not. - JF_EMAIL_RECEIVERS: "eco-system@jfrog.com" diff --git a/.github/workflows/frogbot-scan-pull-request.yml b/.github/workflows/frogbot-scan-pull-request.yml new file mode 100644 index 000000000..998c8c91f --- /dev/null +++ b/.github/workflows/frogbot-scan-pull-request.yml @@ -0,0 +1,119 @@ +name: "Frogbot Scan Pull Request" +on: + pull_request_target: + types: [ opened, synchronize ] +permissions: + pull-requests: write + contents: read +jobs: + scan-pull-request: + runs-on: ubuntu-latest + # A pull request needs to be approved before Frogbot scans it. Any GitHub user who is associated with the + # "frogbot" GitHub environment can approve the pull request to be scanned. + environment: frogbot + steps: + - uses: jfrog/frogbot@v2 + env: + JFROG_CLI_LOG_LEVEL: "DEBUG" + # [Mandatory] + # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) + JF_URL: ${{ secrets.FROGBOT_URL }} + + # [Mandatory if JF_USER and JF_PASSWORD are not provided] + # JFrog access token with 'read' permissions on Xray service + JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} + + # [Mandatory] + # The GitHub token is automatically generated for the job + JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # [Optional, default: https://api.github.com] + # API endpoint to GitHub + # JF_GIT_API_ENDPOINT: https://github.example.com + + # [Optional] + # By default, the Frogbot workflows download the Frogbot executable as well as other tools + # needed from https://releases.jfrog.io + # If the machine that runs Frogbot has no access to the internet, follow these steps to allow the + # executable to be downloaded from an Artifactory instance, which the machine has access to: + # + # 1. Login to the Artifactory UI, with a user who has admin credentials. + # 2. Create a Remote Repository with the following properties set. + # Under the 'Basic' tab: + # Package Type: Generic + # URL: https://releases.jfrog.io + # Under the 'Advanced' tab: + # Uncheck the 'Store Artifacts Locally' option + # 3. Set the value of the 'JF_RELEASES_REPO' variable with the Repository Key you created. + # JF_RELEASES_REPO: "" + + # [Optional] + # Configure the SMTP server to enable Frogbot to send emails with detected secrets in pull request scans. + # SMTP server URL including should the relevant port: (Example: smtp.server.com:8080) + JF_SMTP_SERVER: ${{ secrets.JF_SMTP_SERVER }} + + # [Mandatory if JF_SMTP_SERVER is set] + # The username required for authenticating with the SMTP server. + JF_SMTP_USER: ${{ secrets.JF_SMTP_USER }} + + # [Mandatory if JF_SMTP_SERVER is set] + # The password associated with the username required for authentication with the SMTP server. + JF_SMTP_PASSWORD: ${{ secrets.JF_SMTP_PASSWORD }} + + # [Optional] + # List of comma separated email addresses to receive email notifications about secrets + # detected during pull request scanning. The notification is also sent to the email set + # in the committer git profile regardless of whether this variable is set or not. + JF_EMAIL_RECEIVERS: "eco-system@jfrog.com" + + ########################################################################## + ## If your project uses a 'frogbot-config.yml' file, you can define ## + ## the following variables inside the file, instead of here. ## + ########################################################################## + + # [Mandatory if the two conditions below are met] + # 1. The project uses yarn 2, NuGet or .NET Core to download its dependencies + # 2. The `installCommand` variable isn't set in your frogbot-config.yml file. + # + # The command that installs the project dependencies (e.g "nuget restore") + # JF_INSTALL_DEPS_CMD: "" + + # [Optional, default: "."] + # Relative path to the root of the project in the Git repository + # JF_WORKING_DIR: path/to/project/dir + + # [Optional] + # Xray Watches. Learn more about them here: https://www.jfrog.com/confluence/display/JFROG/Configuring+Xray+Watches + # JF_WATCHES: ,... + + # [Optional] + # JFrog project. Learn more about it here: https://www.jfrog.com/confluence/display/JFROG/Projects + # JF_PROJECT: + + # [Optional, default: "FALSE"] + # Displays all existing vulnerabilities, including the ones that were added by the pull request. + # JF_INCLUDE_ALL_VULNERABILITIES: "TRUE" + + # [Optional, default: "TRUE"] + # Fails the Frogbot task if any security issue is found. + # JF_FAIL: "FALSE" + + # [Optional] + # Frogbot will download the project dependencies if they're not cached locally. To download the + # dependencies from a virtual repository in Artifactory, set the name of the repository. There's no + # need to set this value, if it is set in the frogbot-config.yml file. + # JF_DEPS_REPO: "" + + # [Optional, Default: "FALSE"] + # If TRUE, Frogbot creates a single pull request with all the fixes. + # If false, Frogbot creates a separate pull request for each fix. + # JF_GIT_AGGREGATE_FIXES: "FALSE" + + # [Optional, Default: "FALSE"] + # Handle vulnerabilities with fix versions only + # JF_FIXABLE_ONLY: "TRUE" + + # [Optional] + # Set the minimum severity for vulnerabilities that should be fixed and commented on in pull requests + # The following values are accepted: Low, Medium, High or Critical + # JF_MIN_SEVERITY: "" \ No newline at end of file diff --git a/.github/workflows/frogbot-scan-repository.yml b/.github/workflows/frogbot-scan-repository.yml new file mode 100644 index 000000000..01b568f67 --- /dev/null +++ b/.github/workflows/frogbot-scan-repository.yml @@ -0,0 +1,125 @@ +name: "Frogbot Scan Repository" +on: + workflow_dispatch: + schedule: + # The repository will be scanned once a day at 00:00 GMT. + - cron: "0 0 * * *" +permissions: + contents: write + pull-requests: write + security-events: write +jobs: + scan-repository: + runs-on: ubuntu-latest + strategy: + matrix: + # The repository scanning will be triggered periodically on the following branches. + branch: [ "dev" ] + steps: + - uses: jfrog/frogbot@v2 + env: + JFROG_CLI_LOG_LEVEL: "DEBUG" + # [Mandatory] + # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) + JF_URL: ${{ secrets.FROGBOT_URL }} + + # [Mandatory if JF_USER and JF_PASSWORD are not provided] + # JFrog access token with 'read' permissions on Xray service + JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} + + # [Mandatory if JF_ACCESS_TOKEN is not provided] + # JFrog username with 'read' permissions for Xray. Must be provided with JF_PASSWORD + # JF_USER: ${{ secrets.JF_USER }} + + # [Mandatory if JF_ACCESS_TOKEN is not provided] + # JFrog password. Must be provided with JF_USER + # JF_PASSWORD: ${{ secrets.JF_PASSWORD }} + + # [Mandatory] + # The GitHub token is automatically generated for the job + JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # [Mandatory] + # The name of the branch on which Frogbot will perform the scan + JF_GIT_BASE_BRANCH: ${{ matrix.branch }} + + # [Optional, default: https://api.github.com] + # API endpoint to GitHub + # JF_GIT_API_ENDPOINT: https://github.example.com + + # [Optional] + # By default, the Frogbot workflows download the Frogbot executable as well as other tools + # needed from https://releases.jfrog.io + # If the machine that runs Frogbot has no access to the internet, follow these steps to allow the + # executable to be downloaded from an Artifactory instance, which the machine has access to: + # + # 1. Login to the Artifactory UI, with a user who has admin credentials. + # 2. Create a Remote Repository with the following properties set. + # Under the 'Basic' tab: + # Package Type: Generic + # URL: https://releases.jfrog.io + # Under the 'Advanced' tab: + # Uncheck the 'Store Artifacts Locally' option + # 3. Set the value of the 'JF_RELEASES_REPO' variable with the Repository Key you created. + # JF_RELEASES_REPO: "" + + ########################################################################## + ## If your project uses a 'frogbot-config.yml' file, you can define ## + ## the following variables inside the file, instead of here. ## + ########################################################################## + + # [Optional, default: "."] + # Relative path to the root of the project in the Git repository + # JF_WORKING_DIR: path/to/project/dir + + # [Optional] + # Xray Watches. Learn more about them here: https://www.jfrog.com/confluence/display/JFROG/Configuring+Xray+Watches + # JF_WATCHES: ,... + + # [Optional] + # JFrog project. Learn more about it here: https://www.jfrog.com/confluence/display/JFROG/Projects + # JF_PROJECT: + + # [Optional, default: "TRUE"] + # Fails the Frogbot task if any security issue is found. + # JF_FAIL: "FALSE" + + # [Optional] + # Frogbot will download the project dependencies, if they're not cached locally. To download the + # dependencies from a virtual repository in Artifactory, set the name of the repository. There's no + # need to set this value, if it is set in the frogbot-config.yml file. + # JF_DEPS_REPO: "" + + # [Optional] + # Template for the branch name generated by Frogbot when creating pull requests with fixes. + # The template must include ${BRANCH_NAME_HASH}, to ensure that the generated branch name is unique. + # The template can optionally include the ${IMPACTED_PACKAGE} and ${FIX_VERSION} variables. + # JF_BRANCH_NAME_TEMPLATE: "frogbot-${IMPACTED_PACKAGE}-${BRANCH_NAME_HASH}" + + # [Optional] + # Template for the commit message generated by Frogbot when creating pull requests with fixes + # The template can optionally include the ${IMPACTED_PACKAGE} and ${FIX_VERSION} variables. + # JF_COMMIT_MESSAGE_TEMPLATE: "Upgrade ${IMPACTED_PACKAGE} to ${FIX_VERSION}" + + # [Optional] + # Template for the pull request title generated by Frogbot when creating pull requests with fixes. + # The template can optionally include the ${IMPACTED_PACKAGE} and ${FIX_VERSION} variables. + # JF_PULL_REQUEST_TITLE_TEMPLATE: "[🐸 Frogbot] Upgrade ${IMPACTED_PACKAGE} to ${FIX_VERSION}" + + # [Optional, Default: "FALSE"] + # If TRUE, Frogbot creates a single pull request with all the fixes. + # If FALSE, Frogbot creates a separate pull request for each fix. + # JF_GIT_AGGREGATE_FIXES: "FALSE" + + # [Optional, Default: "FALSE"] + # Handle vulnerabilities with fix versions only + # JF_FIXABLE_ONLY: "TRUE" + + # [Optional] + # Set the minimum severity for vulnerabilities that should be fixed and commented on in pull requests + # The following values are accepted: Low, Medium, High or Critical + # JF_MIN_SEVERITY: "" + + # [Optional, Default: eco-system+frogbot@jfrog.com] + # Set the email of the commit author + # JF_GIT_EMAIL_AUTHOR: "" \ No newline at end of file diff --git a/.github/workflows/goTests.yml b/.github/workflows/goTests.yml index 88ce52f77..1a74d2221 100644 --- a/.github/workflows/goTests.yml +++ b/.github/workflows/goTests.yml @@ -16,12 +16,12 @@ jobs: GO-tests: # Go modules doesn't allow passing credentials to a private registry using an HTTP URL. Therefore, the Go tests run against a remote Artifactory server. if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Go tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/gradleTests.yml b/.github/workflows/gradleTests.yml index 6feac8097..edb2c50f9 100644 --- a/.github/workflows/gradleTests.yml +++ b/.github/workflows/gradleTests.yml @@ -19,9 +19,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ ubuntu, windows, macos ] gradle-version: [5.6.4, 8.3] - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }}-latest env: GRADLE_OPTS: -Dorg.gradle.daemon=false steps: diff --git a/.github/workflows/lifecycleTests.yml b/.github/workflows/lifecycleTests.yml index 01613e0af..0f073b2bd 100644 --- a/.github/workflows/lifecycleTests.yml +++ b/.github/workflows/lifecycleTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: Lifecycle-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Lifecycle tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/mavenTests.yml b/.github/workflows/mavenTests.yml index e9feb1e17..25d0f1f10 100644 --- a/.github/workflows/mavenTests.yml +++ b/.github/workflows/mavenTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: Maven-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Maven tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/npmTests.yml b/.github/workflows/npmTests.yml index 67e14916a..6d0b79663 100644 --- a/.github/workflows/npmTests.yml +++ b/.github/workflows/npmTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: npm-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: npm tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/nugetTests.yml b/.github/workflows/nugetTests.yml index 71b9b71ba..c17fadd48 100644 --- a/.github/workflows/nugetTests.yml +++ b/.github/workflows/nugetTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: NuGet-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: NuGet tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/pluginsTests.yml b/.github/workflows/pluginsTests.yml index 327954a15..a91275a45 100644 --- a/.github/workflows/pluginsTests.yml +++ b/.github/workflows/pluginsTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: Plugins-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Plugins tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/pythonTests.yml b/.github/workflows/pythonTests.yml index f218417ae..d9e7dd0a7 100644 --- a/.github/workflows/pythonTests.yml +++ b/.github/workflows/pythonTests.yml @@ -20,8 +20,8 @@ jobs: fail-fast: false matrix: suite: [ pip, pipenv, poetry ] - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Install Go uses: actions/setup-go@v3 diff --git a/.github/workflows/scriptTests.yml b/.github/workflows/scriptTests.yml index c3991a34f..7201edeeb 100644 --- a/.github/workflows/scriptTests.yml +++ b/.github/workflows/scriptTests.yml @@ -12,15 +12,15 @@ concurrency: cancel-in-progress: true jobs: Scripts-tests: - name: ${{ matrix.os }} + name: Script tests (${{ matrix.os }}) defaults: run: shell: bash strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/transferTests.yml b/.github/workflows/transferTests.yml index 28b98f641..044c6a2e2 100644 --- a/.github/workflows/transferTests.yml +++ b/.github/workflows/transferTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: Transfer-Artifactory-7-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Transfer tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/xrayTests.yml b/.github/workflows/xrayTests.yml index e540c76cc..4adac2e38 100644 --- a/.github/workflows/xrayTests.yml +++ b/.github/workflows/xrayTests.yml @@ -15,12 +15,12 @@ concurrency: jobs: CLI-Tests: if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} + name: Xray tests (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} + os: [ ubuntu, windows, macos ] + runs-on: ${{ matrix.os }}-latest env: GRADLE_OPTS: -Dorg.gradle.daemon=false steps: diff --git a/artifactory_test.go b/artifactory_test.go index 9a5fbe194..390961e66 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + biutils "github.com/jfrog/build-info-go/utils" "io" "net" "net/http" @@ -191,7 +192,7 @@ func TestArtifactorySimpleUploadWithWildcardSpec(t *testing.T) { // Init tmp dir specFile, err := tests.CreateSpec(tests.UploadTempWildcard) assert.NoError(t, err) - err = fileutils.CopyDir(tests.GetTestResourcesPath()+"cache", filepath.Dir(specFile), true, nil) + err = biutils.CopyDir(tests.GetTestResourcesPath()+"cache", filepath.Dir(specFile), true, nil) assert.NoError(t, err) // Upload runRt(t, "upload", "--spec="+specFile) @@ -1536,9 +1537,9 @@ func TestArtifactorySelfSignedCert(t *testing.T) { serverDetails.InsecureTls = false certsPath, err := coreutils.GetJfrogCertsDir() assert.NoError(t, err) - err = fileutils.CopyFile(certsPath, certificate.KeyFile) + err = biutils.CopyFile(certsPath, certificate.KeyFile) assert.NoError(t, err) - err = fileutils.CopyFile(certsPath, certificate.CertFile) + err = biutils.CopyFile(certsPath, certificate.CertFile) assert.NoError(t, err) searchCmd = generic.NewSearchCommand() searchCmd.SetServerDetails(serverDetails).SetSpec(fileSpec) @@ -2281,7 +2282,7 @@ func TestUploadWithArchiveAndSymlink(t *testing.T) { testFile := filepath.Join(tests.GetTestResourcesPath(), "a", "a1.in") tmpDir, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() - err := fileutils.CopyFile(tmpDir, testFile) + err := biutils.CopyFile(tmpDir, testFile) assert.NoError(t, err) // Link valid symLink to local file symlinkTarget := filepath.Join(tmpDir, "a1.in") @@ -5006,7 +5007,7 @@ func TestVcsProps(t *testing.T) { func initVcsTestDir(t *testing.T) string { testdataSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "vcs") testdataTarget := tests.Temp - err := fileutils.CopyDir(testdataSrc, testdataTarget, true, nil) + err := biutils.CopyDir(testdataSrc, testdataTarget, true, nil) assert.NoError(t, err) if found, err := fileutils.IsDirExists(filepath.Join(testdataTarget, "gitdata"), false); found { assert.NoError(t, err) @@ -5420,7 +5421,7 @@ func simpleUploadWithAntPatternSpec(t *testing.T) { // Init tmp dir specFile, err := tests.CreateSpec(tests.UploadAntPattern) assert.NoError(t, err) - err = fileutils.CopyDir(tests.GetTestResourcesPath()+"cache", filepath.Dir(specFile), true, nil) + err = biutils.CopyDir(tests.GetTestResourcesPath()+"cache", filepath.Dir(specFile), true, nil) assert.NoError(t, err) // Upload runRt(t, "upload", "--spec="+specFile) @@ -5446,7 +5447,7 @@ func TestUploadWithAntPatternAndExclusionsSpec(t *testing.T) { // Init tmp dir specFile, err := tests.CreateSpec(tests.UploadAntPatternExclusions) assert.NoError(t, err) - err = fileutils.CopyDir(tests.GetTestResourcesPath(), filepath.Dir(specFile), true, nil) + err = biutils.CopyDir(tests.GetTestResourcesPath(), filepath.Dir(specFile), true, nil) assert.NoError(t, err) // Upload runRt(t, "upload", "--spec="+specFile) @@ -5464,7 +5465,7 @@ func TestUploadWithAntPatternAndPlaceholders(t *testing.T) { // Init tmp dir specFile, err := tests.CreateSpec(tests.UploadAntPatternExclusions) assert.NoError(t, err) - err = fileutils.CopyDir(tests.GetTestResourcesPath(), filepath.Dir(specFile), true, nil) + err = biutils.CopyDir(tests.GetTestResourcesPath(), filepath.Dir(specFile), true, nil) assert.NoError(t, err) // Upload runRt(t, "upload", "--spec="+specFile) @@ -5601,7 +5602,7 @@ func testProjectInit(t *testing.T, projectExampleName string, technology coreuti tmpWorkDir, deleteWorkDir := coretests.CreateTempDirWithCallbackAndAssert(t) defer deleteWorkDir() testdataSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), technology.ToString(), projectExampleName) - err = fileutils.CopyDir(testdataSrc, tmpWorkDir, true, nil) + err = biutils.CopyDir(testdataSrc, tmpWorkDir, true, nil) assert.NoError(t, err) if technology == coreutils.Go { goModeOriginalPath := filepath.Join(tmpWorkDir, "createGoProject_go.mod_suffix") @@ -5700,7 +5701,7 @@ func prepareTerraformProject(projectName string, t *testing.T, copyDirs bool) st testdataTarget := filepath.Join(tests.Out, "terraformProject") assert.NoError(t, os.MkdirAll(testdataTarget+string(os.PathSeparator), 0777)) // Copy terraform tests to test environment, so we can change project's config file. - assert.NoError(t, fileutils.CopyDir(projectPath, testdataTarget, copyDirs, nil)) + assert.NoError(t, biutils.CopyDir(projectPath, testdataTarget, copyDirs, nil)) configFileDir := filepath.Join(filepath.FromSlash(testdataTarget), ".jfrog", "projects") _, err := tests.ReplaceTemplateVariables(filepath.Join(configFileDir, "terraform.yaml"), configFileDir) assert.NoError(t, err) diff --git a/docker_test.go b/docker_test.go index 7f4311374..43b59e310 100644 --- a/docker_test.go +++ b/docker_test.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + biutils "github.com/jfrog/build-info-go/utils" "os" "path" "path/filepath" @@ -482,7 +483,7 @@ func runKaniko(t *testing.T, imageToPush string) string { assert.NoError(t, err) workspace, err := filepath.Abs(tests.Out) assert.NoError(t, err) - assert.NoError(t, fileutils.CopyFile(workspace, filepath.Join(testDir, "docker", dockerFile))) + assert.NoError(t, biutils.CopyFile(workspace, filepath.Join(testDir, "docker", dockerFile))) // Run Kaniko to build the test image and push it to Artifactory. _, err = tests.NewContainerRequest(). diff --git a/go.mod b/go.mod index 737d29c7d..ec2780edd 100644 --- a/go.mod +++ b/go.mod @@ -21,19 +21,20 @@ require ( golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/term v0.11.0 gopkg.in/yaml.v2 v2.4.0 + ) require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/BurntSushi/toml v1.3.2 // indirect - github.com/CycloneDX/cyclonedx-go v0.7.1 // indirect + github.com/CycloneDX/cyclonedx-go v0.7.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.4 // indirect - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.0.1 // indirect github.com/c-bata/go-prompt v0.2.5 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/chzyer/readline v1.5.1 // indirect @@ -56,12 +57,12 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/golang/snappy v0.0.2 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/gookit/color v1.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jedib0t/go-pretty/v6 v6.4.6 // indirect + github.com/jedib0t/go-pretty/v6 v6.4.7 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.16.0 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect @@ -69,7 +70,7 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -84,7 +85,7 @@ require ( github.com/opencontainers/runc v1.1.5 // indirect github.com/owenrumney/go-sarif/v2 v2.2.0 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pierrec/lz4/v4 v4.1.15 // indirect + github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -124,8 +125,10 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230828134416-f0db33dd9344 -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230830130857-c5a2b11b52be +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230831153853-3ddf531482e7 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230830130057-df2d2a80b555 +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230831152946-6ed2ae1aa57f + +replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230831151231-e5e7bd035ddc diff --git a/go.sum b/go.sum index d00b21ab6..4f38367f0 100644 --- a/go.sum +++ b/go.sum @@ -45,14 +45,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CycloneDX/cyclonedx-go v0.7.1 h1:5w1SxjGm9MTMNTuRbEPyw21ObdbaagTWF/KfF0qHTRE= -github.com/CycloneDX/cyclonedx-go v0.7.1/go.mod h1:N/nrdWQI2SIjaACyyDs/u7+ddCkyl/zkNs8xFsHF2Ps= +github.com/CycloneDX/cyclonedx-go v0.7.2 h1:kKQ0t1dPOlugSIYVOMiMtFqeXI2wp/f5DBIdfux8gnQ= +github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -61,9 +61,8 @@ github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -189,9 +188,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -220,8 +218,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -235,16 +233,16 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw= -github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= -github.com/jfrog/build-info-go v1.9.9 h1:YMA9okHawBNL8SrCWzqULSf5M4W+YnWyUhmkWSjoXEE= -github.com/jfrog/build-info-go v1.9.9/go.mod h1:t31QRpH5xUJKw8XkQlAA+Aq7aanyS1rrzpcK8xSNVts= +github.com/jedib0t/go-pretty/v6 v6.4.7 h1:lwiTJr1DEkAgzljsUsORmWsVn5MQjt1BPJdPCtJ6KXE= +github.com/jedib0t/go-pretty/v6 v6.4.7/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= +github.com/jfrog/build-info-go v1.8.9-0.20230831151231-e5e7bd035ddc h1:pqu82clhPKyUKJcljMuxYa+kviaWnHycLNCLqZZNl30= +github.com/jfrog/build-info-go v1.8.9-0.20230831151231-e5e7bd035ddc/go.mod h1:QEskae5fQpjeY2PBzsjWtUQVskYSNDF2sSmw/Gx44dQ= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230830130857-c5a2b11b52be h1:MjbSKQy937o0WFBKCXtvkX4EUSPCaA1LIhGISJUjYbU= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230830130857-c5a2b11b52be/go.mod h1:kaFzB3X83/jdzMcuGOYOaqnlz5MVTnDYt/asrOsCv18= -github.com/jfrog/jfrog-client-go v1.28.1-0.20230830130057-df2d2a80b555 h1:yaF5J4LNk+ws5+j+BFMJ43NMqpKuCtF7dUfCeGLttl8= -github.com/jfrog/jfrog-client-go v1.28.1-0.20230830130057-df2d2a80b555/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230831153853-3ddf531482e7 h1:G60mVnOYqtVJ0UQpkYS2kmbqYZ59q1g8XivZt9W0tUg= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230831153853-3ddf531482e7/go.mod h1:eQJU+jDTcPVqOeHNheURqkMEMXDVNQFKKjcyVEa0058= +github.com/jfrog/jfrog-client-go v1.28.1-0.20230831152946-6ed2ae1aa57f h1:S6l0o2sKFLRJ+QYVB5U/PJhrnwFSmKFFY7eHpRPRH8A= +github.com/jfrog/jfrog-client-go v1.28.1-0.20230831152946-6ed2ae1aa57f/go.mod h1:uUnMrqHX7Xi+OCaZEE4b3BtsmGeOSCB7XqaEWVXEH/E= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk= @@ -283,9 +281,8 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -324,9 +321,8 @@ github.com/owenrumney/go-sarif/v2 v2.2.0 h1:1DmZaijK0HBZCR1fgcDSGa7VzYkU9NDmbZ7q github.com/owenrumney/go-sarif/v2 v2.2.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= @@ -385,6 +381,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -594,7 +591,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/go_test.go b/go_test.go index 1b00960c0..0d558121f 100644 --- a/go_test.go +++ b/go_test.go @@ -2,6 +2,7 @@ package main import ( "fmt" + biutils "github.com/jfrog/build-info-go/utils" "os" "os/exec" "path/filepath" @@ -279,7 +280,7 @@ func prepareGoProject(projectName string, t *testing.T, copyDirs bool) string { projectPath := createGoProject(t, projectName, copyDirs) testdataTarget := filepath.Join(tests.Out, "testdata") testdataSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "go", "testdata") - err := fileutils.CopyDir(testdataSrc, testdataTarget, copyDirs, nil) + err := biutils.CopyDir(testdataSrc, testdataTarget, copyDirs, nil) assert.NoError(t, err) configFileDir := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "go", projectName, ".jfrog", "projects") _, err = tests.ReplaceTemplateVariables(filepath.Join(configFileDir, "go.yaml"), filepath.Join(projectPath, ".jfrog", "projects")) @@ -326,12 +327,13 @@ func createTempGoPath(t *testing.T) (tempGoPath string, cleanUp func()) { func createGoProject(t *testing.T, projectName string, includeDirs bool) string { projectSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "go", projectName) projectTarget := filepath.Join(tests.Out, projectName) - err := fileutils.CopyDir(projectSrc, projectTarget, includeDirs, nil) + err := biutils.CopyDir(projectSrc, projectTarget, includeDirs, nil) assert.NoError(t, err) projectTarget, err = filepath.Abs(projectTarget) assert.NoError(t, err) goModeOriginalPath := filepath.Join(projectTarget, "createGoProject_go.mod_suffix") goModeTargetPath := filepath.Join(projectTarget, "go.mod") + assert.NoError(t, os.Chmod(goModeOriginalPath, 0700)) assert.NoError(t, os.Rename(goModeOriginalPath, goModeTargetPath)) return projectTarget } diff --git a/main_test.go b/main_test.go index bc6684073..96efc1218 100644 --- a/main_test.go +++ b/main_test.go @@ -221,9 +221,9 @@ func createConfigFileForTest(dirs []string, resolver, deployer string, t *testin // Create config file to make sure the path is valid f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) assert.NoError(t, err, "Couldn't create file") - defer func() { - assert.NoError(t, f.Close()) - }() + defer func(file *os.File) { + assert.NoError(t, file.Close()) + }(f) _, err = f.Write(d) assert.NoError(t, err) } diff --git a/maven_test.go b/maven_test.go index e7de3a582..16311cb5c 100644 --- a/maven_test.go +++ b/maven_test.go @@ -3,6 +3,7 @@ package main import ( "fmt" buildinfo "github.com/jfrog/build-info-go/entities" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/mvn" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/common/commands" @@ -13,7 +14,6 @@ import ( "github.com/jfrog/jfrog-cli/utils/tests" cliproxy "github.com/jfrog/jfrog-cli/utils/tests/proxy/server" "github.com/jfrog/jfrog-cli/utils/tests/proxy/server/certificate" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" @@ -178,7 +178,7 @@ func createMultiMavenProject(t *testing.T) string { return "" } destPath = filepath.Join(destPath, tests.Temp) - assert.NoError(t, fileutils.CopyDir(projectDir, destPath, true, nil)) + assert.NoError(t, biutils.CopyDir(projectDir, destPath, true, nil)) return destPath } diff --git a/npm_test.go b/npm_test.go index 6cc36806e..1a5e4a683 100644 --- a/npm_test.go +++ b/npm_test.go @@ -11,7 +11,8 @@ import ( "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/yarn" - biutils "github.com/jfrog/build-info-go/build/utils" + buildutils "github.com/jfrog/build-info-go/build/utils" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/gofrog/version" coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-client-go/utils/log" @@ -69,7 +70,7 @@ func testNpm(t *testing.T, isLegacy bool) { wd, err := os.Getwd() assert.NoError(t, err, "Failed to get current dir") defer clientTestUtils.ChangeDirAndAssert(t, wd) - npmVersion, _, err := biutils.GetNpmVersionAndExecPath(log.Logger) + npmVersion, _, err := buildutils.GetNpmVersionAndExecPath(log.Logger) if err != nil { assert.NoError(t, err) return @@ -134,7 +135,7 @@ func testNpm(t *testing.T, isLegacy bool) { } func readModuleId(t *testing.T, wd string, npmVersion *version.Version) string { - packageInfo, err := biutils.ReadPackageInfoFromPackageJson(filepath.Dir(wd), npmVersion) + packageInfo, err := buildutils.ReadPackageInfoFromPackageJson(filepath.Dir(wd), npmVersion) assert.NoError(t, err) return packageInfo.BuildInfoModuleId() } @@ -171,7 +172,7 @@ func TestNpmConditionalUpload(t *testing.T) { assert.NoError(t, err, "Failed to get current dir") searchSpec, err := tests.CreateSpec(tests.SearchAllNpm) assert.NoError(t, err) - npmVersion, _, err := biutils.GetNpmVersionAndExecPath(log.Logger) + npmVersion, _, err := buildutils.GetNpmVersionAndExecPath(log.Logger) assert.NoError(t, err) npmProjectPath := initNpmProjectTest(t) clientTestUtils.ChangeDirAndAssert(t, npmProjectPath) @@ -350,7 +351,7 @@ func TestNpmPublishDetailedSummary(t *testing.T) { assert.NoError(t, err, "Failed to get current dir") defer clientTestUtils.ChangeDirAndAssert(t, wd) - npmVersion, _, err := biutils.GetNpmVersionAndExecPath(log.Logger) + npmVersion, _, err := buildutils.GetNpmVersionAndExecPath(log.Logger) if err != nil { assert.NoError(t, err) return @@ -460,7 +461,7 @@ func TestYarn(t *testing.T) { testDataSource := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "yarn") testDataTarget := filepath.Join(tempDirPath, tests.Out, "yarn") - assert.NoError(t, fileutils.CopyDir(testDataSource, testDataTarget, true, nil)) + assert.NoError(t, biutils.CopyDir(testDataSource, testDataTarget, true, nil)) yarnProjectPath := filepath.Join(testDataTarget, "yarnproject") assert.NoError(t, createConfigFileForTest([]string{yarnProjectPath}, tests.NpmRemoteRepo, "", t, utils.Yarn, false)) diff --git a/nuget_test.go b/nuget_test.go index b77f106b5..1f5f498b1 100644 --- a/nuget_test.go +++ b/nuget_test.go @@ -4,6 +4,7 @@ import ( "encoding/xml" dotnetUtils "github.com/jfrog/build-info-go/build/utils/dotnet" buildInfo "github.com/jfrog/build-info-go/entities" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/dotnet" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" @@ -93,7 +94,7 @@ func createNugetProject(t *testing.T, projectName string) string { err := fileutils.CreateDirIfNotExist(projectTarget) assert.NoError(t, err) - err = fileutils.CopyDir(projectSrc, projectTarget, true, nil) + err = biutils.CopyDir(projectSrc, projectTarget, true, nil) assert.NoError(t, err) return projectTarget } diff --git a/pip_test.go b/pip_test.go index e466d2d25..f7bcc9a96 100644 --- a/pip_test.go +++ b/pip_test.go @@ -1,6 +1,7 @@ package main import ( + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-cli-core/v2/xray/audit/python" @@ -168,7 +169,7 @@ func createPipProject(t *testing.T, outFolder, projectName string) string { assert.NoError(t, err) // Copy pip-installation file. - err = fileutils.CopyDir(projectSrc, projectTarget, true, nil) + err = biutils.CopyDir(projectSrc, projectTarget, true, nil) assert.NoError(t, err) // Copy pip-config file. diff --git a/pipenv_test.go b/pipenv_test.go index 0200eb0cb..f5b7b9bc6 100644 --- a/pipenv_test.go +++ b/pipenv_test.go @@ -1,6 +1,7 @@ package main import ( + biutils "github.com/jfrog/build-info-go/utils" "os" "path/filepath" "strconv" @@ -112,7 +113,7 @@ func createPipenvProject(t *testing.T, outFolder, projectName string) string { assert.NoError(t, err) // Copy pipenv-installation file. - err = fileutils.CopyDir(projectSrc, projectTarget, true, nil) + err = biutils.CopyDir(projectSrc, projectTarget, true, nil) assert.NoError(t, err) // Copy pipenv-config file. diff --git a/plugins/commands/uninstall_test.go b/plugins/commands/uninstall_test.go index 4aa5b975d..dfee60776 100644 --- a/plugins/commands/uninstall_test.go +++ b/plugins/commands/uninstall_test.go @@ -1,6 +1,7 @@ package commands import ( + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/utils/log" "github.com/jfrog/jfrog-cli-core/v2/utils/plugins" @@ -39,7 +40,7 @@ func TestRunUninstallCmd(t *testing.T) { return } pluginName := filepath.Base(pluginMockPath) - err = fileutils.CopyDir(pluginMockPath, filepath.Join(pluginsDir, pluginName), true, nil) + err = biutils.CopyDir(pluginMockPath, filepath.Join(pluginsDir, pluginName), true, nil) if err != nil { assert.NoError(t, err) return diff --git a/plugins_test.go b/plugins_test.go index 83e51eb77..dcbe0e187 100644 --- a/plugins_test.go +++ b/plugins_test.go @@ -1,6 +1,7 @@ package main import ( + biutils "github.com/jfrog/build-info-go/utils" "os" "path" "path/filepath" @@ -254,7 +255,7 @@ func TestPublishInstallCustomServer(t *testing.T) { exists, err := fileutils.IsDirExists(filepath.Join(wd, coreutils.PluginsResourcesDirName), false) assert.NoError(t, err) assert.False(t, exists) - err = fileutils.CopyDir(filepath.Join(wd, "testdata", "plugins", "plugin-mock", coreutils.PluginsResourcesDirName), filepath.Join(wd, coreutils.PluginsResourcesDirName), true, nil) + err = biutils.CopyDir(filepath.Join(wd, "testdata", "plugins", "plugin-mock", coreutils.PluginsResourcesDirName), filepath.Join(wd, coreutils.PluginsResourcesDirName), true, nil) assert.NoError(t, err) // Test with resources directory testPublishAndInstall(t, true) diff --git a/poetry_test.go b/poetry_test.go index 89194e1dc..3188ad34d 100644 --- a/poetry_test.go +++ b/poetry_test.go @@ -1,6 +1,7 @@ package main import ( + biutils "github.com/jfrog/build-info-go/utils" "os" "path/filepath" "strconv" @@ -108,7 +109,7 @@ func createPoetryProject(t *testing.T, outFolder, projectName string) string { assert.NoError(t, err) // Copy poetry project - err = fileutils.CopyDir(projectSrc, projectTarget, true, nil) + err = biutils.CopyDir(projectSrc, projectTarget, true, nil) assert.NoError(t, err) // Copy poetry-config file. diff --git a/testdata/go/dependency/createGoProject_go.mod_suffix b/testdata/go/dependency/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/project1/createGoProject_go.mod_suffix b/testdata/go/project1/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/project2/createGoProject_go.mod_suffix b/testdata/go/project2/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/project3/createGoProject_go.mod_suffix b/testdata/go/project3/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/project4/createGoProject_go.mod_suffix b/testdata/go/project4/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/projectbuild/createGoProject_go.mod_suffix b/testdata/go/projectbuild/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/projectmissingdependency/createGoProject_go.mod_suffix b/testdata/go/projectmissingdependency/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/go/vcsfallback/createGoProject_go.mod_suffix b/testdata/go/vcsfallback/createGoProject_go.mod_suffix old mode 100644 new mode 100755 diff --git a/testdata/npm/npmnpmrcproject/.npmrc b/testdata/npm/npmnpmrcproject/.npmrc old mode 100644 new mode 100755 diff --git a/testdata/npm/npmnpmrcproject/package.json b/testdata/npm/npmnpmrcproject/package.json old mode 100644 new mode 100755 diff --git a/testdata/npm/npmpostinstall/package.json b/testdata/npm/npmpostinstall/package.json old mode 100644 new mode 100755 diff --git a/testdata/npm/npmpostinstall/subdir/package.json b/testdata/npm/npmpostinstall/subdir/package.json old mode 100644 new mode 100755 diff --git a/testdata/npm/npmproject/package.json b/testdata/npm/npmproject/package.json old mode 100644 new mode 100755 diff --git a/testdata/npm/npmprojectci/package.json b/testdata/npm/npmprojectci/package.json old mode 100644 new mode 100755 diff --git a/testdata/npm/npmscopedproject/package.json b/testdata/npm/npmscopedproject/package.json old mode 100644 new mode 100755 diff --git a/testdata/xray/gradle/api/build.gradle b/testdata/xray/gradle/api/build.gradle new file mode 100644 index 000000000..492158b15 --- /dev/null +++ b/testdata/xray/gradle/api/build.gradle @@ -0,0 +1,15 @@ +configurations { + spi +} + +dependencies { + implementation project(':shared') + implementation module("commons-lang:commons-lang:2.4") { + dependency("commons-io:commons-io:1.2") + } + implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7' + +} + +// Just a smoke test that using this option does not lead to any exception +compileJava.options.compilerArgs = ['-Xlint:unchecked'] diff --git a/testdata/xray/gradle/api/src/main/java/org/gradle/api/PersonList.java b/testdata/xray/gradle/api/src/main/java/org/gradle/api/PersonList.java new file mode 100644 index 000000000..1d80a16b3 --- /dev/null +++ b/testdata/xray/gradle/api/src/main/java/org/gradle/api/PersonList.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 JFrog Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.api; + +import org.gradle.apiImpl.Impl; +import org.gradle.shared.Person; + +import java.util.ArrayList; + + +public class PersonList { + private ArrayList persons = new ArrayList(); + + public void doSomethingWithImpl() { + org.apache.commons.lang.builder.ToStringBuilder stringBuilder; + try { + Class.forName("org.apache.commons.io.FileUtils"); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + new Impl().implMethod(); + } + +} diff --git a/testdata/xray/gradle/api/src/main/java/org/gradle/api/package.html b/testdata/xray/gradle/api/src/main/java/org/gradle/api/package.html new file mode 100644 index 000000000..cea998a80 --- /dev/null +++ b/testdata/xray/gradle/api/src/main/java/org/gradle/api/package.html @@ -0,0 +1,19 @@ + + + +

These are the API classes

+ diff --git a/testdata/xray/gradle/api/src/main/java/org/gradle/apiImpl/Impl.java b/testdata/xray/gradle/api/src/main/java/org/gradle/apiImpl/Impl.java new file mode 100644 index 000000000..76cf0cc22 --- /dev/null +++ b/testdata/xray/gradle/api/src/main/java/org/gradle/apiImpl/Impl.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2011 JFrog Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.apiImpl; + + +public class Impl { + + public void implMethod() { + double a = 4.0 * 4; + } + +} diff --git a/testdata/xray/gradle/build.gradle b/testdata/xray/gradle/build.gradle index e1b9e8fd3..1ae1ed814 100644 --- a/testdata/xray/gradle/build.gradle +++ b/testdata/xray/gradle/build.gradle @@ -1,21 +1,33 @@ -apply plugin: 'groovy' -apply plugin: 'idea' +/* + * Copyright (C) 2013 JFrog Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -version = 1.0 -task initProject(description: 'Initialize project directory structure.') { - doLast { - // Default package to be created in each src dir. - def defaultPackage = 'org/jfrog/minimal-project' +buildscript { + repositories { + mavenCentral() + } +} - ['java', 'groovy', 'resources'].each { - // convention.sourceSets contains the directory structure - // for our Groovy project. So we use this structure - // and make a directory for each node. - sourceSets*."${it}".srcDirs*.each { dir -> - def newDir = new File(dir, defaultPackage) - logger.info "Creating directory $newDir" // gradle -i shows this message. - newDir.mkdirs() // Create dir. - } - } +allprojects { + repositories { + mavenCentral() } -} \ No newline at end of file + apply plugin: 'java' + apply plugin: 'maven-publish' + + group = 'org.jfrog.example.gradle' + version = '1.0' + status = 'integration' +} diff --git a/testdata/xray/gradle/gradle/wrapper/gradle-wrapper.jar b/testdata/xray/gradle/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 0000000000000000000000000000000000000000..5c2d1cf016b3885f6930543d57b744ea8c220a1a GIT binary patch literal 55616 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/testdata/xray/gradle/gradlew.bat b/testdata/xray/gradle/gradlew.bat new file mode 100755 index 000000000..9618d8d96 --- /dev/null +++ b/testdata/xray/gradle/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testdata/xray/gradle/services/webservice/build.gradle b/testdata/xray/gradle/services/webservice/build.gradle new file mode 100644 index 000000000..f3ded41f2 --- /dev/null +++ b/testdata/xray/gradle/services/webservice/build.gradle @@ -0,0 +1,8 @@ +apply plugin: 'war' + +dependencies { + implementation project(':shared'), 'commons-collections:commons-collections:3.2@jar', 'commons-io:commons-io:1.2', 'commons-lang:commons-lang:2.4@jar' + implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7' + testImplementation group: 'junit', name: 'junit', version: '4.11' + implementation project(':api') +} diff --git a/testdata/xray/gradle/services/webservice/src/main/java/org/gradle/webservice/TestTest.java b/testdata/xray/gradle/services/webservice/src/main/java/org/gradle/webservice/TestTest.java new file mode 100644 index 000000000..4843cca41 --- /dev/null +++ b/testdata/xray/gradle/services/webservice/src/main/java/org/gradle/webservice/TestTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 JFrog Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.webservice; + +import org.apache.commons.collections.list.GrowthList; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.gradle.api.PersonList; +import org.gradle.shared.Person; + +public class TestTest { + private String name; + + public void method() { + FilenameUtils.separatorsToUnix("my/unix/filename"); + ToStringBuilder.reflectionToString(new Person("name")); + new GrowthList(); + new PersonList().doSomethingWithImpl(); // compile with api-spi, runtime with api + } + +} diff --git a/testdata/xray/gradle/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java b/testdata/xray/gradle/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java new file mode 100644 index 000000000..b8e22a4ad --- /dev/null +++ b/testdata/xray/gradle/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.gradle.webservice; + +import junit.framework.TestCase; + +/** + * @author Hans Dockter + */ +public class TestTestTest extends TestCase { + public void testClasspath() { + new TestTest().method(); + } + + public void testApiCompileClasspath() { + new org.gradle.api.PersonList(); + } +} diff --git a/testdata/xray/gradle/settings.gradle b/testdata/xray/gradle/settings.gradle index 6cf974940..397a3021a 100644 --- a/testdata/xray/gradle/settings.gradle +++ b/testdata/xray/gradle/settings.gradle @@ -1 +1 @@ -rootProject.name = 'minimal-example' \ No newline at end of file +include "shared", "api", "services:webservice" diff --git a/testdata/xray/gradle/shared/src/main/java/org/gradle/shared/Person.java b/testdata/xray/gradle/shared/src/main/java/org/gradle/shared/Person.java new file mode 100644 index 000000000..d9ed11ee8 --- /dev/null +++ b/testdata/xray/gradle/shared/src/main/java/org/gradle/shared/Person.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 JFrog Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradle.shared; + +import java.io.IOException; +import java.util.Properties; + +public class Person { + private String name; + + public Person(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String readProperty() throws IOException { + Properties properties = new Properties(); + properties.load(getClass().getClassLoader().getResourceAsStream("org/gradle/shared/main.properties")); + return properties.getProperty("main"); + } +} diff --git a/testdata/xray/gradle/shared/src/main/java/org/gradle/shared/package-info.java b/testdata/xray/gradle/shared/src/main/java/org/gradle/shared/package-info.java new file mode 100644 index 000000000..7c5f65caf --- /dev/null +++ b/testdata/xray/gradle/shared/src/main/java/org/gradle/shared/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2011 JFrog Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * These are the shared classes. + */ +package org.gradle.shared; diff --git a/testdata/xray/gradle/shared/src/main/resources/org/gradle/shared/main.properties b/testdata/xray/gradle/shared/src/main/resources/org/gradle/shared/main.properties new file mode 100644 index 000000000..fe6422c8d --- /dev/null +++ b/testdata/xray/gradle/shared/src/main/resources/org/gradle/shared/main.properties @@ -0,0 +1,16 @@ +# +# Copyright (C) 2011 JFrog Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +main=mainValue diff --git a/testdata/xray/jas/iac/azure/vpc/module.tf b/testdata/xray/jas-test/iac/azure/vpc/module.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc/module.tf rename to testdata/xray/jas-test/iac/azure/vpc/module.tf diff --git a/testdata/xray/jas/iac/azure/vpc/outputs.tf b/testdata/xray/jas-test/iac/azure/vpc/outputs.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc/outputs.tf rename to testdata/xray/jas-test/iac/azure/vpc/outputs.tf diff --git a/testdata/xray/jas/iac/azure/vpc/variables.tf b/testdata/xray/jas-test/iac/azure/vpc/variables.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc/variables.tf rename to testdata/xray/jas-test/iac/azure/vpc/variables.tf diff --git a/testdata/xray/jas/iac/azure/vpc/versions.tf b/testdata/xray/jas-test/iac/azure/vpc/versions.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc/versions.tf rename to testdata/xray/jas-test/iac/azure/vpc/versions.tf diff --git a/testdata/xray/jas/iac/azure/vpc_pp/module.tf b/testdata/xray/jas-test/iac/azure/vpc_pp/module.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc_pp/module.tf rename to testdata/xray/jas-test/iac/azure/vpc_pp/module.tf diff --git a/testdata/xray/jas/iac/azure/vpc_pp/outputs.tf b/testdata/xray/jas-test/iac/azure/vpc_pp/outputs.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc_pp/outputs.tf rename to testdata/xray/jas-test/iac/azure/vpc_pp/outputs.tf diff --git a/testdata/xray/jas/iac/azure/vpc_pp/variables.tf b/testdata/xray/jas-test/iac/azure/vpc_pp/variables.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc_pp/variables.tf rename to testdata/xray/jas-test/iac/azure/vpc_pp/variables.tf diff --git a/testdata/xray/jas/iac/azure/vpc_pp/versions.tf b/testdata/xray/jas-test/iac/azure/vpc_pp/versions.tf similarity index 100% rename from testdata/xray/jas/iac/azure/vpc_pp/versions.tf rename to testdata/xray/jas-test/iac/azure/vpc_pp/versions.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-oss/files/chk_k8s_nat b/testdata/xray/jas-test/iac/gcp/k8s-oss/files/chk_k8s_nat similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-oss/files/chk_k8s_nat rename to testdata/xray/jas-test/iac/gcp/k8s-oss/files/chk_k8s_nat diff --git a/testdata/xray/jas/iac/gcp/k8s-oss/module.tf b/testdata/xray/jas-test/iac/gcp/k8s-oss/module.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-oss/module.tf rename to testdata/xray/jas-test/iac/gcp/k8s-oss/module.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-oss/outputs.tf b/testdata/xray/jas-test/iac/gcp/k8s-oss/outputs.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-oss/outputs.tf rename to testdata/xray/jas-test/iac/gcp/k8s-oss/outputs.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-oss/variables.tf b/testdata/xray/jas-test/iac/gcp/k8s-oss/variables.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-oss/variables.tf rename to testdata/xray/jas-test/iac/gcp/k8s-oss/variables.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-oss/versions.tf b/testdata/xray/jas-test/iac/gcp/k8s-oss/versions.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-oss/versions.tf rename to testdata/xray/jas-test/iac/gcp/k8s-oss/versions.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat b/testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat rename to testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat diff --git a/testdata/xray/jas/iac/gcp/k8s-pipelines-bp/module.tf b/testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/module.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-pipelines-bp/module.tf rename to testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/module.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-pipelines-bp/outputs.tf b/testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/outputs.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-pipelines-bp/outputs.tf rename to testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/outputs.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-pipelines-bp/rbac.tf b/testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/rbac.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-pipelines-bp/rbac.tf rename to testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/rbac.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-pipelines-bp/variables.tf b/testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/variables.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-pipelines-bp/variables.tf rename to testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/variables.tf diff --git a/testdata/xray/jas/iac/gcp/k8s-pipelines-bp/versions.tf b/testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/versions.tf similarity index 100% rename from testdata/xray/jas/iac/gcp/k8s-pipelines-bp/versions.tf rename to testdata/xray/jas-test/iac/gcp/k8s-pipelines-bp/versions.tf diff --git a/testdata/xray/jas/main.py b/testdata/xray/jas-test/main.py similarity index 100% rename from testdata/xray/jas/main.py rename to testdata/xray/jas-test/main.py diff --git a/testdata/xray/jas/requirements.txt b/testdata/xray/jas-test/requirements.txt similarity index 100% rename from testdata/xray/jas/requirements.txt rename to testdata/xray/jas-test/requirements.txt diff --git a/testdata/xray/jas/secrets/more_secrets/key b/testdata/xray/jas-test/secrets/more_secrets/key similarity index 100% rename from testdata/xray/jas/secrets/more_secrets/key rename to testdata/xray/jas-test/secrets/more_secrets/key diff --git a/testdata/xray/jas/secrets/more_secrets/sequence b/testdata/xray/jas-test/secrets/more_secrets/sequence similarity index 100% rename from testdata/xray/jas/secrets/more_secrets/sequence rename to testdata/xray/jas-test/secrets/more_secrets/sequence diff --git a/testdata/xray/jas/secrets/secret_generic/blacklist b/testdata/xray/jas-test/secrets/secret_generic/blacklist similarity index 100% rename from testdata/xray/jas/secrets/secret_generic/blacklist rename to testdata/xray/jas-test/secrets/secret_generic/blacklist diff --git a/testdata/xray/jas/secrets/secret_generic/gibberish b/testdata/xray/jas-test/secrets/secret_generic/gibberish similarity index 100% rename from testdata/xray/jas/secrets/secret_generic/gibberish rename to testdata/xray/jas-test/secrets/secret_generic/gibberish diff --git a/transfer_test.go b/transfer_test.go index 1ac5ce147..40c735552 100644 --- a/transfer_test.go +++ b/transfer_test.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + biutils "github.com/jfrog/build-info-go/utils" "os" "path/filepath" "strconv" @@ -397,7 +398,7 @@ func generateSnapshotFiles(t *testing.T) (repoSnapshotDir string) { assert.NoError(t, err) repoState, err := state.GetRepoStateFilepath(tests.RtRepo1, false) assert.NoError(t, err) - assert.NoError(t, fileutils.CopyFile(repoSnapshotDir, repoState)) + assert.NoError(t, biutils.CopyFile(repoSnapshotDir, repoState)) // Create snapshot file at snapshots directory. repoSnapshotFile, err := state.GetRepoSnapshotFilePath(tests.RtRepo1) diff --git a/utils/cliutils/utils_test.go b/utils/cliutils/utils_test.go index c3cef052d..0d257bb5f 100644 --- a/utils/cliutils/utils_test.go +++ b/utils/cliutils/utils_test.go @@ -3,6 +3,7 @@ package cliutils import ( "errors" "fmt" + biutils "github.com/jfrog/build-info-go/utils" configtests "github.com/jfrog/jfrog-cli-core/v2/utils/config/tests" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "path/filepath" @@ -14,7 +15,6 @@ import ( commandUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils" "github.com/jfrog/jfrog-client-go/utils/io/content" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/stretchr/testify/assert" ) @@ -50,7 +50,7 @@ func TestPrintCommandSummary(t *testing.T) { testdata := filepath.Join(tests.GetTestResourcesPath(), "reader", "printcommandsummary.json") tmpDir, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() - err := fileutils.CopyFile(tmpDir, testdata) + err := biutils.CopyFile(tmpDir, testdata) assert.NoError(t, err) reader := content.NewContentReader(filepath.Join(tmpDir, "printcommandsummary.json"), content.DefaultKey) diff --git a/xray_test.go b/xray_test.go index d1638b483..fd3ff3150 100644 --- a/xray_test.go +++ b/xray_test.go @@ -5,6 +5,7 @@ import ( "errors" "flag" "fmt" + biutils "github.com/jfrog/build-info-go/utils" "net/http" "net/http/httptest" "os" @@ -152,7 +153,7 @@ func testXrayAuditNpm(t *testing.T, format string) string { defer createTempDirCallback() npmProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "npm") // Copy the npm project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(npmProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(npmProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Run npm install before executing jfrog xr npm-audit @@ -195,7 +196,7 @@ func testXrayAuditYarn(t *testing.T, projectDirName string, yarnCmd func()) { defer createTempDirCallback() yarnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", projectDirName) // Copy the Yarn project from the testdata to a temp directory - assert.NoError(t, fileutils.CopyDir(yarnProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(yarnProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Run yarn install before executing jf audit --yarn. Return error to assert according to test. @@ -232,7 +233,7 @@ func testXrayAuditNuget(t *testing.T, projectName, format string) string { defer createTempDirCallback() projectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "nuget", projectName) - assert.NoError(t, fileutils.CopyDir(projectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(projectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit @@ -244,12 +245,12 @@ func testXrayAuditNuget(t *testing.T, projectName, format string) string { func TestXrayAuditGradleJson(t *testing.T) { output := testXrayAuditGradle(t, string(utils.Json)) - verifyJsonScanResults(t, output, 0, 0, 0) + verifyJsonScanResults(t, output, 0, 3, 3) } func TestXrayAuditGradleSimpleJson(t *testing.T) { output := testXrayAuditGradle(t, string(utils.SimpleJson)) - verifySimpleJsonScanResults(t, output, 0, 0) + verifySimpleJsonScanResults(t, output, 3, 3) } func testXrayAuditGradle(t *testing.T, format string) string { @@ -258,7 +259,7 @@ func testXrayAuditGradle(t *testing.T, format string) string { defer createTempDirCallback() gradleProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "gradle") // Copy the gradle project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(gradleProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(gradleProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit @@ -282,7 +283,7 @@ func testXrayAuditMaven(t *testing.T, format string) string { defer createTempDirCallback() mvnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "maven") // Copy the maven project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(mvnProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(mvnProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit @@ -307,7 +308,7 @@ func TestXrayAuditDetectTech(t *testing.T) { defer createTempDirCallback() mvnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "maven") // Copy the maven project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(mvnProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(mvnProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Run generic audit on mvn project with a vulnerable dependency @@ -325,10 +326,10 @@ func TestXrayAuditMultiProjects(t *testing.T) { defer createTempDirCallback() multiProject := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray") // Copy the multi project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(multiProject, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(multiProject, tempDirPath, true, nil)) workingDirsFlag := fmt.Sprintf("--working-dirs=%s, %s ,%s, %s", filepath.Join(tempDirPath, "maven"), filepath.Join(tempDirPath, "nuget", "single"), - filepath.Join(tempDirPath, "python", "pip"), filepath.Join(tempDirPath, "jas")) + filepath.Join(tempDirPath, "python", "pip"), filepath.Join(tempDirPath, "jas-test")) // Configure a new server named "default" createJfrogHomeConfig(t, true) defer cleanTestsHomeEnv() @@ -363,7 +364,7 @@ func testXrayAuditPip(t *testing.T, format, requirementsFile string) string { defer createTempDirCallback() pipProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "pip") // Copy the pip project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(pipProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(pipProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit @@ -392,7 +393,7 @@ func testXrayAuditPipenv(t *testing.T, format string) string { defer createTempDirCallback() pipenvProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "pipenv") // Copy the pipenv project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(pipenvProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(pipenvProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit @@ -454,7 +455,7 @@ func testXrayAuditPoetry(t *testing.T, format string) string { defer createTempDirCallback() poetryProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "poetry") // Copy the poetry project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(poetryProjectPath, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(poetryProjectPath, tempDirPath, true, nil)) prevWd := changeWD(t, tempDirPath) defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit @@ -667,7 +668,7 @@ func TestXrayOfflineDBSyncV3(t *testing.T) { } func TestXrayAuditJasSimpleJson(t *testing.T) { - output := testXrayAuditJas(t, string(utils.SimpleJson), "jas") + output := testXrayAuditJas(t, string(utils.SimpleJson), "jas-test") verifySimpleJsonJasResults(t, output, 9, 7, 2, 1) } @@ -683,7 +684,7 @@ func testXrayAuditJas(t *testing.T, format string, project string) string { defer createTempDirCallback() projectDir := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), filepath.Join("xray", project)) // Copy the multi project from the testdata to a temp dir - assert.NoError(t, fileutils.CopyDir(projectDir, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(projectDir, tempDirPath, true, nil)) // Configure a new server named "default" createJfrogHomeConfig(t, true) defer cleanTestsHomeEnv() @@ -718,7 +719,7 @@ func TestCurationAudit(t *testing.T) { tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() multiProject := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray") - assert.NoError(t, fileutils.CopyDir(multiProject, tempDirPath, true, nil)) + assert.NoError(t, biutils.CopyDir(multiProject, tempDirPath, true, nil)) rootDir, err := os.Getwd() require.NoError(t, err) defer func() { From aa56aa9be64a6e662e9301085157fa4760717ad4 Mon Sep 17 00:00:00 2001 From: Michael Sverdlov Date: Thu, 31 Aug 2023 19:11:07 +0300 Subject: [PATCH 7/7] Update xrayTests.yml (#2178) --- .github/workflows/xrayTests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/xrayTests.yml b/.github/workflows/xrayTests.yml index 4adac2e38..83d8a6f64 100644 --- a/.github/workflows/xrayTests.yml +++ b/.github/workflows/xrayTests.yml @@ -63,7 +63,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Run Xray tests run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --jfrog.user=${{ secrets.PLATFORM_USER }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} - if: ${{ matrix.os != 'ubuntu-latest' }} + if: ${{ matrix.os != 'ubuntu' }} - name: Run Docker scan and Xray tests run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --test.dockerScan --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} - if: ${{ matrix.os == 'ubuntu-latest' }} \ No newline at end of file + if: ${{ matrix.os == 'ubuntu' }}