jfrog / jfrog-cli

JFrog CLI is a client that provides a simple interface that automates access to the JFrog products.
https://www.jfrog.com/confluence/display/CLI/JFrog+CLI
Apache License 2.0
532 stars 231 forks source link

Illegal build-publish output - `Dependency id name cannot be null!` #2723

Open ckristo opened 1 week ago

ckristo commented 1 week ago

Describe the bug

Hey,

I'm trying to publish build infos for my python3.11 project to Artifactory, but get an error at the jf rt bp step:

[project-pipeline_develop] $ jf rt bp
15:37:05 [Debug] JFrog CLI version: 2.63.1
15:37:05 [Debug] OS/Arch: linux/amd64
15:37:05 [Debug] Trace ID for JFrog Platform logs: ********
15:37:05 [Debug] Usage Report: Sending info...
15:37:05 [Debug] Reading build general details from: /tmp/jfrog/builds/c4fdfb8d9e6ff1e5c5e7d58190f18d34e20c9a884170aff1a20d6694962a0b6f/partials
15:37:05 [Debug] Sending HTTP GET request to: https://jfrog.platform.internal/artifactory/api/system/version
15:37:05 [Info] Deploying build info...
15:37:05 [Debug] Sending HTTP PUT request to: https://jfrog.platform.internal/artifactory/api/build
15:37:05 [Debug] Artifactory response: 200 
15:37:05 [Debug] JFrog Artifactory version is: 7.71.23
15:37:05 [Debug] Sending HTTP POST request to: https://jfrog.platform.internal/artifactory/api/system/usage
15:37:05 [Info] Trace ID for JFrog Platform logs: ********
15:37:05 [Error] server response: 400 
{
  "errors": [
    {
      "status": 400,
      "message": "Dependency id name cannot be null!"
    }
  ]
}

Current behavior

Enabling JFrog CLI debug mode and setting the dry-run option gives me the following build job output:

[project-pipeline_develop] $ jf rt bp --dry-run
15:37:05 [Debug] JFrog CLI version: 2.63.1
15:37:05 [Debug] OS/Arch: linux/amd64
15:37:05 [Debug] Trace ID for JFrog Platform logs: ********
15:37:05 [Debug] Creating lock in: /opt/work/workspace/project-pipeline_develop@tmp/jfrog/109/.jfrog/locks/config
15:37:05 [Debug] Sending HTTP POST request to: https://jfrog-platform.internal/artifactory/api/security/token
15:37:05 [Debug] Releasing lock: /opt/work/workspace/project-pipeline_develop@tmp/jfrog/109/.jfrog/locks/config/jfrog-cli.conf.lck.415.1728913025047188974
15:37:05 [Debug] Usage Report: Sending info...
15:37:05 [Debug] Reading build general details from: /tmp/jfrog/builds/c4fdfb8d9e6ff1e5c5e7d58190f18d34e20c9a884170aff1a20d6694962a0b6f/partials
15:37:05 [Debug] Sending HTTP GET request to: https://jfrog-platform.internal/artifactory/api/system/version
15:37:05 [Info] [Dry run] Logging Build info preview...
15:37:05 [Debug] Artifactory response: 200 
15:37:05 [Debug] JFrog Artifactory version is: 7.71.23
15:37:05 [Debug] Sending HTTP POST request to: https://jfrog-platform.internal/artifactory/api/system/usage
{
  "name": "project_name-develop",
  "number": "1",
  "agent": {
    "name": "jfrog-cli-go",
    "version": "2.63.1"
  },
  "buildAgent": {
    "name": "GENERIC",
    "version": "2.63.1"
  },
  "modules": [
    {
      "type": "python",
      "id": "module_name:0.0.1.dev",
      "dependencies": [
        {
          "sha1": "ccb7a74c114522f26e2c3b1884468343f54098d3",
          "md5": "07349a98333a31cbb6ef8f7a17905c77",
          "sha256": "44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"
        },
        {
          "sha1": "fd4fd35bc1f4926f73aeb3ee3a6cbaeb6117de4f",
          "md5": "ef5cf00ede2a18c500438cb8a94b6d62",
          "sha256": "5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf"
        },
        // ...
        {
          "id": "zipp-3.20.2-py3-none-any.whl",
          "sha1": "837304f58ca231257f74e44221b0cdff8ad4c9c8",
          "md5": "b96cde46ce0c9dcecfe645f53427e715",
          "sha256": "a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"
        },
        {
          "id": "google_auth-2.35.0-py2.py3-none-any.whl",
          "sha1": "79d18c33089e120434741bcc6b27a19f85a7e21e",
          "md5": "caf5161901251bbff5ad74eb55ac1013",
          "sha256": "25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"
        },
        // ...
      ]
    }
  ],
  "started": "2024-10-14T15:36:08.889+0200",
  "properties": {
    "buildInfo.env.VAR1": "val1",
    "buildInfo.env.VAR2": "val2",
    // ...
  },
  "artifactoryPrincipal": "user-id",
  "url": "https://jenkins.internal/buildUrl...",
  "vcs": [
    {
      "url": "ssh://git@git.internal:2345/namespace/module_name.git",
      "revision": "86873e5b98a51ed0a0f3611e1a57dc473443a778",
      "message": "commit message"
    }
  ]
}
Illegal build-publish output: 15:37:05 [Debug] JFrog CLI version: 2.63.1
15:37:05 [Debug] OS/Arch: linux/amd64
15:37:05 [Debug] Trace ID for JFrog Platform logs: d0dbf97c60fffce1
15:37:05 [Debug] Creating lock in: /opt/work/workspace/project-pipeline_develop@tmp/jfrog/109/.jfrog/locks/config
15:37:05 [Debug] Sending HTTP POST request to: https://jfrog-platform.internal/artifactory/api/security/token
15:37:05 [Debug] Releasing lock: /opt/work/workspace/project-pipeline_develop@tmp/jfrog/109/.jfrog/locks/config/jfrog-cli.conf.lck.415.1728913025047188974
15:37:05 [Debug] Usage Report: Sending info...
15:37:05 [Debug] Reading build general details from: /tmp/jfrog/builds/c4fdfb8d9e6ff1e5c5e7d58190f18d34e20c9a884170aff1a20d6694962a0b6f/partials
15:37:05 [Debug] Sending HTTP GET request to: https://jfrog-platform.internal/artifactory/api/system/version
15:37:05 [Info] [Dry run] Logging Build info preview...
15:37:05 [Debug] Artifactory response: 200 
15:37:05 [Debug] JFrog Artifactory version is: 7.71.23
15:37:05 [Debug] Sending HTTP POST request to: https://jfrog-platform.internal/artifactory/api/system/usage
// ... same JSON output as above, verified via diff ...

JsonEOFException: Unexpected end-of-input: expected close marker for Object (start marker at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1])
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 7, column: 4]

Looking at the modules.dependencies JSON structure, the first n elements indeed miss an id field. What I do not understand yet is the JsonEOFException shown at the end of the dry run output.

Reproduction steps

#!/usr/bin/env groovy

pipeline {

    agent { label 'python311' }

    environment {
        ARTIFACTORY_REPO_NAME = 'pypi-repo'
        PYPI_REPO_URL = "https://jfrog-platform.internal/artifactory/api/pypi/${ARTIFACTORY_REPO_NAME}"
        PATH = "/opt/jenkins/.local/bin:${env.PATH}"
        SOLUTION_CODE = 'id'
        PROJECT_NAME = 'project_name'
        PROJECT_KEY = "${SOLUTION_CODE}-${PROJECT_NAME}"
        JFROG_CLI_BUILD_NAME = "${PROJECT_KEY}-${BRANCH_NAME}"
        JFROG_CLI_BUILD_NUMBER = "${BUILD_NUMBER}"
        JFROG_CLI_LOG_LEVEL = 'DEBUG'
    }

    stages {

        stage('Setup') {
            steps {
                sh 'python -m pip install --index ${PYPI_REPO_URL}/simple --upgrade pip'
                jf "pipc --repo-resolve=${ARTIFACTORY_REPO_NAME}"
                jf "pip install -r requirements.txt"
                jf "pip install ."
                // ...
            }
        }

        // stage('Test')

        stage('Build package') {
            steps {
                sh 'python -m build --verbose'
                archiveArtifacts artifacts: 'dist/*', fingerprint: true
            }
        }

        // stage('SAST scan')

        stage('SCA scan') {
            steps {
                jf 'c show'
                jf 'rt bag'
                jf 'rt bce'
                jf 'rt bp'
                jf 'bs'
            }
        }

        // stage('Deploy package')

    }

    post {
        always {
            sh 'rm -rf build dist *.egg-info .pytest_cache .coverage htmlcov coverage.xml .jfrog'
        }
    }

}

jf c show shows:

[_analyzer_build-pipeline_develop] $ jf c show
Server ID:          Internal-JFrog
JFrog Platform URL:     https://jfrog-platform.internal/
Artifactory URL:        https://jfrog-platform.internal/artifactory/
Distribution URL:       https://jfrog-platform.internal/distribution/
Xray URL:           https://jfrog-platform.internal/xray/
Mission Control URL:        https://jfrog-platform.internal/mc/
Pipelines URL:          https://jfrog-platform.internal/pipelines/
User:               user-id
Password:           ***
Default:            true

Expected behavior

JFrog CLI should only generate valid build infos which can be published to Artifactory (in my case: all dependencies should have an id field).

JFrog CLI version

2.59.1

Operating system type and version

Linux

JFrog Artifactory version

7.71.23

JFrog Xray version

No response

ckristo commented 6 days ago

I switched to use venv (which was a challenge, but I think it works now fine) and got a working version. However, I encountered that I can also break the venv version by making slight changes in the Setup stage.

Here the working version:

#!/usr/bin/env groovy

pipeline {

    agent { label 'python311-preview' }

    environment {
        ARTIFACTORY_REPO_NAME = 'pypi-repo'
        PYPI_REPO_URL = "https://jfrog-platform.internal/artifactory/api/pypi/${ARTIFACTORY_REPO_NAME}"
        VENV_ENV_DIR = '.venv'
        VIRTUAL_ENV = "${env.WORKSPACE}/${VENV_ENV_DIR}"
        PATH = "${VIRTUAL_ENV}/bin:/opt/jenkins/.local/bin:${PATH}"
        SOLUTION_CODE = 'app-id'
        PROJECT_NAME = 'module_name'
        PROJECT_KEY = "${SOLUTION_CODE}-${PROJECT_NAME}"
        JFROG_CLI_BUILD_NAME = "${PROJECT_KEY}-${BRANCH_NAME}"
        JFROG_CLI_BUILD_NUMBER = "${BUILD_NUMBER}"
        JFROG_CLI_LOG_LEVEL = 'DEBUG'
    }

    stages {

        stage('Setup') {
            steps {
                sh '''
                    python -m venv $VENV_ENV_DIR
                    source $VENV_ENV_DIR/bin/activate
                    pip config set global.index-url "${PYPI_REPO_URL}/simple"
                    pip install --upgrade pip
                    pip install build twine setuptools setuptools-scm
                '''
                script {
                    def stdout = sh script: 'python -m setuptools_scm', returnStdout: true
                    env.VERSION = stdout.trim()
                }
                jf 'c show'
                jf "pipc --global --repo-resolve=${ARTIFACTORY_REPO_NAME}"
                jf "pip install ."
            }
        }

        stage('Test') {
            steps {
                sh "pip install '.[test]'"
                sh 'pytest'
                tar dir: 'htmlcov', compress: true, file: "${env.PROJECT_NAME}-${env.VERSION}-htmlcov.tgz", archive: true, overwrite: true
            }
        }

        stage('Build package') {
            steps {
                sh 'python -m build --verbose'
                archiveArtifacts artifacts: 'dist/*', fingerprint: true
            }
        }

        // stage('SAST scan') {

        stage('SCA scan') {
            steps {
                jf 'rt bag'
                jf 'rt bce'
                jf 'rt bp'
                jf 'bs'
                script {
                    try {
                        jf 'aud --pip --watches DA-All-build-watch'
                    } catch (err) {
                        unstable(message: "JFrog CLI SCA via `audit` command indicated build failure: ${err}")
                        // NOTE: do not fail on `jf audit` because this is additional info for devs only
                        //       fail should be indicated by `jf bs`
                    }
                }
            }
        }

        stage('Deploy package') {
            when {
                anyOf {
                    allOf {
                        branch 'master'
                        expression {
                            def tag = sh script: 'git tag --points-at ${GIT_COMMIT}', returnStdout: true
                            return tag.startsWith('v')
                        }
                    }
                    tag 'v*'
                }
            }
            steps {
                jf 'twine upload "dist/*"'
                /*
                script {

                    def stdout = sh script: 'python -m twine upload --verbose --skip-existing --disable-progress-bar --non-interactive --repository-url $PYPI_REPO_URL dist/* -u$XRAY_USERNAME -p$XRAY_PASSWORD', returnStdout: true
                    stdout = stdout.trim()
                    println stdout
                    if (stdout.contains('WARNING')) {
                        unstable(message: "${STAGE_NAME} issued warning(s)")
                    }
                }
                */
            }
        }

    }

    post {
        always {
            sh 'rm -rf build dist *.egg-info .pytest_cache .coverage htmlcov coverage.xml .scannerwork .jfrog $VENV_ENV_DIR'
        }
    }

}

If I change the Setup stage as follows, I get the same issue again:

@@ -27,15 +27,15 @@
                     source $VENV_ENV_DIR/bin/activate
                     pip config set global.index-url "${PYPI_REPO_URL}/simple"
                     pip install --upgrade pip
-                    pip install build twine setuptools setuptools_scm
                 '''
+                jf 'c show'
+                jf "pipc --global --repo-resolve=${ARTIFACTORY_REPO_NAME}"
+                jf 'pip install build twine setuptools setuptools-scm'
+                jf "pip install ."
                 script {
                     def stdout = sh script: 'python -m setuptools_scm', returnStdout: true
                     env.VERSION = stdout.trim()
                 }
-                jf 'c show'
-                jf "pipc --global --repo-resolve=${ARTIFACTORY_REPO_NAME}"
-                jf "pip install ."
             }
         }

It looks like there is some issue when packages are already installed or cached somehow...

ckristo commented 6 days ago

I updated the Jenkins plugin and JFrog CLI to their latest versions (1.5.5 and 2.71.0) and can confirm that the issue still persists.

ckristo commented 5 days ago

Seems this is a duplicate of #2366.