Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.codeant.ai/llms.txt

Use this file to discover all available pages before exploring further.

Jenkins Pipeline

Add the following stage to your Jenkinsfile. It triggers on pushes to the configured branches:
pipeline {
    agent any
    
    environment {
        ACCESS_TOKEN = credentials('ACCESS_TOKEN')
        API_BASE = 'https://api.codeant.ai'
    }
    
    stages {
        stage('Test Coverage') {
            steps {
                script {
                    // Extract repository information
                    def repoUrl = env.GIT_URL
                    def repoName = ""
                    
                    if (repoUrl && repoUrl.contains('github.com')) {
                        repoName = repoUrl.replaceAll(/.*github\.com[\/:]([^\/]+\/[^\/]+)\.git.*/, '$1')
                    } else if (repoUrl && repoUrl.contains('gitlab.com')) {
                        repoName = repoUrl.replaceAll(/.*gitlab\.com[\/:]([^\/]+\/[^\/]+)\.git.*/, '$1')
                    } else {
                        // Fallback: use a configured value or extract from job name
                        repoName = "org/repo_name" // Replace with your actual repo
                    }
                    
                    def branchName = env.GIT_BRANCH.replaceAll(/^origin\//, '')
                    
                    echo "Running tests and generating coverage"
                    sh """
                        # Activate virtual environment if exists
                        if [ -f .venv/bin/activate ]; then
                            source .venv/bin/activate
                        fi
                        
                        # Install coverage tools
                        pip install coverage pytest
                        
                        # Run tests with coverage
                        coverage run -m pytest tests/
                        coverage xml -o coverage.xml
                        
                        # Display coverage report
                        cat coverage.xml
                    """
                    
                    echo "Fetching coverage-upload script"
                    sh """
                        curl -sS -X GET "${API_BASE}/pr/analysis/coverage/script/get" \\
                            --output upload_coverage.sh.b64
                    """
                    
                    echo " Making script executable"
                    sh """
                        base64 -d upload_coverage.sh.b64 > upload_coverage.sh
                        chmod +x upload_coverage.sh
                    """
                    
                    echo "Uploading coverage and setting status"
                    sh """
                        bash upload_coverage.sh \\
                            -t "${ACCESS_TOKEN}" \\
                            -r "${repoName}" \\
                            -c "${env.GIT_COMMIT}" \\
                            -f coverage.xml \\
                            -p github \\
                            -b "${branchName}"
                    """
                }
            }
            post {
                always {
                    // Archive coverage report
                    archiveArtifacts artifacts: 'coverage.xml', allowEmptyArchive: true
                    
                    // Clean up temporary files
                    sh """
                        rm -f upload_coverage.sh upload_coverage.sh.b64
                    """
                }
                success {
                    echo " Coverage report uploaded successfully"
                }
                failure {
                    echo "Failed to upload coverage report"
                }
            }
        }
    }
}
For monorepo projects, add the module name parameter -m module_name to the upload script. To upload coverage for multiple modules from a single commit, invoke upload_coverage.sh once per module — each invocation with its own -f, -m, and -d. A single invocation with multiple -m flags will only honor the last value and attribute every file to that one module. See Multiple Coverage Files (Monorepo) below for the recommended setup.

Multiple Coverage Files (Monorepo)

When a single commit produces several coverage reports — for example one per service in a monorepo — give each upload its own -m module_name and -d module_path. CodeAnt AI keeps the reports separate so each module is tracked, displayed, and gated independently. Without a module name, every upload writes to the same key and later stages overwrite earlier ones. Jenkins declarative pipelines support a matrix directive that fans a stage out across axis values, with each cell running in parallel. Define the upload once and let the matrix generate one cell per module.
pipeline {
    agent any

    environment {
        ACCESS_TOKEN = credentials('ACCESS_TOKEN')
        API_BASE = 'https://api.codeant.ai'
    }

    stages {
        stage('Fetch upload script') {
            steps {
                sh '''
                    curl -sS -X GET "${API_BASE}/pr/analysis/coverage/script/get" \
                        --output upload_coverage.sh.b64
                    base64 -d upload_coverage.sh.b64 > upload_coverage.sh
                    chmod +x upload_coverage.sh
                '''
            }
        }

        stage('Coverage per module') {
            matrix {
                axes {
                    axis {
                        name 'MODULE'
                        values 'backend', 'frontend', 'api'
                    }
                }
                stages {
                    stage('Test & upload') {
                        environment {
                            // Map MODULE → MODULE_PATH and COVERAGE_FILE.
                            // For uniform layouts, derive these in the shell.
                            MODULE_PATH = "services/${MODULE}"
                            COVERAGE_FILE = "services/${MODULE}/coverage.xml"
                        }
                        steps {
                            sh '''
                                cd "${MODULE_PATH}"
                                pip install coverage pytest
                                coverage run -m pytest tests/
                                coverage xml -o coverage.xml
                                cd -

                                bash upload_coverage.sh \
                                    -t "${ACCESS_TOKEN}" \
                                    -r "org/repo_name" \
                                    -c "${GIT_COMMIT}" \
                                    -f "${COVERAGE_FILE}" \
                                    -p github \
                                    -b "${GIT_BRANCH#origin/}" \
                                    -m "${MODULE}" \
                                    -d "${MODULE_PATH}"
                            '''
                        }
                    }
                }
            }
        }
    }

    post {
        always {
            sh 'rm -f upload_coverage.sh upload_coverage.sh.b64'
        }
    }
}
Jenkins generates one cell per matrix value — MODULE = backend, MODULE = frontend, etc. — and runs them in parallel. Adding a new module is one new value in the axis. If your MODULE_PATH or COVERAGE_FILE can’t be derived uniformly from the module name, use explicit parallel stages instead (see below).
Branch resolution: ${GIT_BRANCH#origin/} works for classic freestyle/pipeline jobs that check out a single ref. On multibranch pipelines, use env.BRANCH_NAME instead; on PR builds (via the GitHub/Bitbucket Branch Source plugins), env.CHANGE_BRANCH holds the source branch while env.BRANCH_NAME is set to PR-<n>. Swap the -b argument to whichever variable your job type populates.

Alternative: explicit parallel stages

If you prefer the explicit form (e.g. each module has a fully different test command, or paths and files don’t follow a convention), declare each upload as its own stage and run them with parallel:
stage('Upload coverage') {
    parallel {
        stage('backend') {
            steps {
                sh '''
                    bash upload_coverage.sh \
                        -t "${ACCESS_TOKEN}" \
                        -r "org/repo_name" \
                        -c "${GIT_COMMIT}" \
                        -f "services/backend/coverage.xml" \
                        -p github -b "${GIT_BRANCH#origin/}" \
                        -m "backend" -d "services/backend"
                '''
            }
        }
        stage('frontend') {
            steps {
                sh '''
                    bash upload_coverage.sh \
                        -t "${ACCESS_TOKEN}" \
                        -r "org/repo_name" \
                        -c "${GIT_COMMIT}" \
                        -f "services/frontend/coverage/cobertura-coverage.xml" \
                        -p github -b "${GIT_BRANCH#origin/}" \
                        -m "frontend" -d "services/frontend"
                '''
            }
        }
    }
}

Credentials Configuration

Add your access token to Jenkins credentials:
  1. Go to Manage Jenkins -> Credentials -> System -> Global credentials
  2. Click Add Credentials
  3. Select Secret text as the kind
  4. Enter ACCESS_TOKEN as the ID
  5. Paste your Github access token as the secret
  6. Add a description like “GitHub Access Token”
  7. Click Create

Token Permissions

The access token requires the following permissions:
  • Metadata: Read-only access
  • Commit statuses: Read and write access
These permissions allow the token to access repository metadata and update commit status checks for coverage reporting.

Coverage config file

You have to create a .coveragerc file in the project’s root folder to include all the source files in the test coverage calculation. Example:
# include every Python file under the repo root
source = .

# exclude tests, virtualenvs, build artifacts, etc.
omit =
    */tests/*
    */.venv/*
    */build/*
    */dist/*
When you assign source to ”.” , It checks for every python file in the root folder and its sub directories. You can omit some directories by placing them in the omit section of the file.

How it works

With the above configuration:
  1. coverage run -m pytest tests/ will count every .py under the workspace as “valid” lines except for those in the omitted directories.
  2. Lines actually executed by your tests are marked “covered.”
  3. coverage xml -o coverage.xml produces a Cobertura-style report reflecting true coverage over the entire codebase.
  4. Using this coverage xml, we calculate the coverage percentage and the status check will be done on every new push to the branch.

Troubleshooting

Coverage file not found

If the coverage file is not generated:
  • Verify tests are running successfully
  • Check that coverage is installed: pip install coverage
  • Ensure the correct test command is used
  • Check the working directory

Upload fails with authentication error

If you see “Access token invalid”:
  • Verify the ACCESS_TOKEN credential is correctly configured
  • Ensure the token has the required permissions
  • Check that the credential ID matches exactly

Coverage percentage is 0%

If coverage shows 0%:
  • Check your .coveragerc configuration
  • Verify the source paths are correct
  • Ensure tests are actually exercising the code
  • Check omit patterns aren’t excluding too much

Branch name extraction issues

If the branch name is incorrect:
  • Debug with echo "Branch: ${env.GIT_BRANCH}"
  • Handle different branch name formats
  • Consider using env.BRANCH_NAME for multibranch pipelines