# Use jscpd in CI

> Run jscpd in GitHub Actions, GitLab CI, and other CI systems to enforce duplication thresholds.

## GitHub Action

The [jscpd-copy-paste-detector](https://github.com/marketplace/actions/jscpd-copy-paste-detector) GitHub Action runs jscpd in your CI workflow. It installs the Rust engine, runs detection, uploads SARIF to GitHub Code Scanning, and optionally uploads the report as an artifact.

### Basic Usage

```yaml [.github/workflows/jscpd.yml]
name: Duplication Check

on: [push, pull_request]

jobs:
  jscpd:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: kucherenko/jscpd@master
```

This scans the entire repository with default settings and uploads SARIF results to GitHub Code Scanning.

### Fail on Threshold

Set `threshold` to fail the build when duplication exceeds a percentage:

```yaml
- uses: kucherenko/jscpd@master
  with:
    threshold: 5
```

The workflow fails if more than 5% of the code is duplicated.

### Action Inputs

<table>
<thead>
  <tr>
    <th>
      Input
    </th>
    
    <th>
      Description
    </th>
    
    <th>
      Default
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        path
      </code>
    </td>
    
    <td>
      Paths to scan (space-separated)
    </td>
    
    <td>
      <code>
        .
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        config
      </code>
    </td>
    
    <td>
      Path to <code>
        .jscpd.json
      </code>
      
       config file
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        min-tokens
      </code>
    </td>
    
    <td>
      Minimum tokens for a clone
    </td>
    
    <td>
      <code>
        50
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        min-lines
      </code>
    </td>
    
    <td>
      Minimum lines for a clone
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-lines
      </code>
    </td>
    
    <td>
      Maximum lines per block
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      Detection mode: <code>
        mild
      </code>
      
      , <code>
        weak
      </code>
      
      , <code>
        strict
      </code>
    </td>
    
    <td>
      <code>
        mild
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        format
      </code>
    </td>
    
    <td>
      Comma-separated formats to check
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ignore
      </code>
    </td>
    
    <td>
      Comma-separated glob patterns to ignore
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ignore-pattern
      </code>
    </td>
    
    <td>
      Comma-separated regex patterns to skip
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reporters
      </code>
    </td>
    
    <td>
      Comma-separated reporters
    </td>
    
    <td>
      <code>
        console
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        output
      </code>
    </td>
    
    <td>
      Output directory for file reporters
    </td>
    
    <td>
      <code>
        report
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threshold
      </code>
    </td>
    
    <td>
      Max duplication % before exit 1
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        blame
      </code>
    </td>
    
    <td>
      Enrich clones with git blame data
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exit-code
      </code>
    </td>
    
    <td>
      Exit with code when duplicates found (<code>
        true
      </code>
      
       or integer)
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pattern
      </code>
    </td>
    
    <td>
      Glob pattern for file search
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-size
      </code>
    </td>
    
    <td>
      Skip files larger than SIZE
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        skip-local
      </code>
    </td>
    
    <td>
      Skip clones in same directory
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ignore-case
      </code>
    </td>
    
    <td>
      Ignore case of symbols (experimental)
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        follow-symlinks
      </code>
    </td>
    
    <td>
      Follow symbolic links
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        no-gitignore
      </code>
    </td>
    
    <td>
      Don't respect .gitignore files
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        absolute
      </code>
    </td>
    
    <td>
      Use absolute paths in reports
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        formats-exts
      </code>
    </td>
    
    <td>
      Custom format-to-extension mappings
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        formats-names
      </code>
    </td>
    
    <td>
      Custom format-to-filename mappings
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        version
      </code>
    </td>
    
    <td>
      jscpd version to install
    </td>
    
    <td>
      <code>
        latest
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        install-prefix
      </code>
    </td>
    
    <td>
      Installation directory for the binary
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        skip-install
      </code>
    </td>
    
    <td>
      Skip installation (binary already present)
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        extra-args
      </code>
    </td>
    
    <td>
      Additional arguments passed to jscpd
    </td>
    
    <td>
      —
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        upload-report
      </code>
    </td>
    
    <td>
      Upload report directory as artifact
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        upload-sarif
      </code>
    </td>
    
    <td>
      Upload SARIF to GitHub Code Scanning
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
  </tr>
</tbody>
</table>

### Action Outputs

<table>
<thead>
  <tr>
    <th>
      Output
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        duplication-percentage
      </code>
    </td>
    
    <td>
      Percentage of duplicated code found
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        clones-found
      </code>
    </td>
    
    <td>
      Number of clone pairs found
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duplicated-lines
      </code>
    </td>
    
    <td>
      Number of duplicated lines
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        total-lines
      </code>
    </td>
    
    <td>
      Total lines scanned
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        files-count
      </code>
    </td>
    
    <td>
      Number of source files scanned
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        report-path
      </code>
    </td>
    
    <td>
      Path to the output directory
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sarif-path
      </code>
    </td>
    
    <td>
      Path to the SARIF report file
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exit-code
      </code>
    </td>
    
    <td>
      Exit code from jscpd
    </td>
  </tr>
</tbody>
</table>

### Examples

#### Scan specific directories with threshold

```yaml
- uses: kucherenko/jscpd@master
  with:
    path: src/lib src/utils
    threshold: 3
    ignore: "**/*.test.*,**/*.spec.*"
```

#### Use a config file

```yaml
- uses: kucherenko/jscpd@master
  with:
    config: .jscpd.json
    upload-report: true
```

#### Multi-reporter with artifact upload

```yaml
- uses: kucherenko/jscpd@master
  with:
    reporters: console,json,html,sarif
    output: jscpd-report
    upload-report: true
```

#### Pin a specific version

```yaml
- uses: kucherenko/jscpd@master
  with:
    version: "5.0.9"
```

#### Skip install (binary already in image)

```yaml
- uses: kucherenko/jscpd@master
  with:
    skip-install: true
```

#### Use outputs in subsequent steps

```yaml
- uses: kucherenko/jscpd@master
  id: jscpd

- name: Check results
  if: steps.jscpd.outputs.duplication-percentage > 5
  run: |
    echo "Duplication is ${{ steps.jscpd.outputs.duplication-percentage }}%"
    echo "Found ${{ steps.jscpd.outputs.clones-found }} clones"
```

## GitLab CI

Use jscpd in GitLab CI with a simple pipeline job:

```yaml [.gitlab-ci.yml]
jscpd:
  stage: test
  image: node:20
  before_script:
    - npm install -g jscpd@5
  script:
    - jscpd --threshold 5 --reporters console,sarif .
  artifacts:
    paths:
      - report/
```

## Generic CI

Any CI system that can run shell commands works with jscpd:

```bash
npm install -g jscpd@5
jscpd --threshold 5 ./src
```

The `--threshold` flag makes jscpd exit with code 1 when duplication exceeds the specified percentage, which causes CI builds to fail.

### Tips

- Use `--reporters console,sarif` in CI to get both console output and SARIF for code scanning platforms
- Use `--threshold` to set a failure threshold — the process exits with code 1 if exceeded
- Use `--ignore` to exclude generated files, test fixtures, or vendor directories
- For large repos, use the Rust engine (`jscpd@5` / `cpd`) — it runs 24-37x faster, keeping CI times low
- Consider `--format` to limit detection to specific languages during CI, with a full scan in a scheduled job
