[{"data":1,"prerenderedAt":1413},["ShallowReactive",2],{"navigation_docs":3,"-guides-ci":239,"-guides-ci-surround":1408},[4,29,94,114,184],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"Start",false,"\u002Fstart","1.start",[10,14,19,24],{"title":11,"path":7,"stem":12,"icon":13},"What jscpd finds","1.start\u002Findex","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fstart\u002Finstallation","1.start\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Quickstart","\u002Fstart\u002Fquickstart","1.start\u002F3.quickstart","i-lucide-rocket",{"title":25,"path":26,"stem":27,"icon":28},"Configuration basics","\u002Fstart\u002Fconfiguration","1.start\u002F4.configuration","i-lucide-settings-2",{"title":30,"icon":6,"path":31,"stem":32,"children":33,"page":6},"Guides","\u002Fguides","2.guides",[34,39,44,49,54,59,64,69,74,79,84,89],{"title":35,"path":36,"stem":37,"icon":38},"Use jscpd in CI","\u002Fguides\u002Fci","2.guides\u002F01.ci","i-lucide-github",{"title":40,"path":41,"stem":42,"icon":43},"Pre-commit hooks","\u002Fguides\u002Fpre-commit","2.guides\u002F02.pre-commit","i-lucide-git-commit",{"title":45,"path":46,"stem":47,"icon":48},"Fail only on new duplication","\u002Fguides\u002Fbaseline","2.guides\u002F03.baseline","i-lucide-git-compare",{"title":50,"path":51,"stem":52,"icon":53},"Monorepos","\u002Fguides\u002Fmonorepos","2.guides\u002F04.monorepos","i-lucide-folder-tree",{"title":55,"path":56,"stem":57,"icon":58},"Cross-format detection","\u002Fguides\u002Fcross-format","2.guides\u002F05.cross-format","i-lucide-layers",{"title":60,"path":61,"stem":62,"icon":63},"Agents","\u002Fguides\u002Fagents","2.guides\u002F06.agents","i-lucide-cpu",{"title":65,"path":66,"stem":67,"icon":68},"Editors","\u002Fguides\u002Feditors","2.guides\u002F07.editors","i-lucide-code",{"title":70,"path":71,"stem":72,"icon":73},"Duplication over git history","\u002Fguides\u002Fhistory","2.guides\u002F08.history","i-lucide-chart-no-axes-combined",{"title":75,"path":76,"stem":77,"icon":78},"Dashboard and health score","\u002Fguides\u002Fdashboard","2.guides\u002F09.dashboard","i-lucide-layout-dashboard",{"title":80,"path":81,"stem":82,"icon":83},"Dead code","\u002Fguides\u002Fdead-code","2.guides\u002F10.dead-code","i-lucide-ghost",{"title":85,"path":86,"stem":87,"icon":88},"Comparing two codebases","\u002Fguides\u002Fcompare","2.guides\u002F11.compare","i-lucide-git-compare-arrows",{"title":90,"path":91,"stem":92,"icon":93},"Semantic clones","\u002Fguides\u002Fsemantic-clones","2.guides\u002F12.semantic-clones","i-lucide-brain-circuit",{"title":95,"icon":6,"path":96,"stem":97,"children":98,"page":6},"Concepts","\u002Fconcepts","3.concepts",[99,104,109],{"title":100,"path":101,"stem":102,"icon":103},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":105,"path":106,"stem":107,"icon":108},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":110,"path":111,"stem":112,"icon":113},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":115,"icon":6,"path":116,"stem":117,"children":118,"page":6},"Reference","\u002Freference","4.reference",[119,124,129,134,171,176,180],{"title":120,"path":121,"stem":122,"icon":123},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":125,"path":126,"stem":127,"icon":128},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":130,"path":131,"stem":132,"icon":133},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":135,"icon":6,"path":136,"stem":137,"children":138},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[139,141,146,151,156,161,166],{"title":135,"path":136,"stem":137,"icon":140},"i-lucide-file-chart-column",{"title":142,"path":143,"stem":144,"icon":145},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":147,"path":148,"stem":149,"icon":150},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":152,"path":153,"stem":154,"icon":155},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":157,"path":158,"stem":159,"icon":160},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":162,"path":163,"stem":164,"icon":165},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":167,"path":168,"stem":169,"icon":170},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":172,"path":173,"stem":174,"icon":175},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":177,"path":178,"stem":179,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":181,"path":182,"stem":183,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":185,"icon":6,"path":186,"stem":187,"children":188,"page":6},"Project","\u002Fproject","5.project",[189,194,199,204,229,234],{"title":190,"path":191,"stem":192,"icon":193},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":195,"path":196,"stem":197,"icon":198},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":200,"path":201,"stem":202,"icon":203},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":205,"icon":6,"path":206,"stem":207,"children":208},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[209,211,216,220,225],{"title":205,"path":206,"stem":207,"icon":210},"i-lucide-gauge",{"title":212,"path":213,"stem":214,"icon":215},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":217,"path":218,"stem":219,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":221,"path":222,"stem":223,"icon":224},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":226,"path":227,"stem":228,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":230,"path":231,"stem":232,"icon":233},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":235,"path":236,"stem":237,"icon":238},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",{"id":240,"title":35,"body":241,"description":1400,"extension":1401,"links":1402,"meta":1403,"navigation":1404,"path":36,"seo":1405,"stem":37,"__hash__":1407},"docs\u002F2.guides\u002F01.ci.md",{"type":242,"value":243,"toc":1378},"minimark",[244,257,262,337,360,396,404,414,418,423,561,587,591,701,735,739,761,782,807,811,815,920,972,976,1045,1077,1081,1142,1185,1188,1215,1219,1223,1261,1267,1286,1290,1299,1303,1313,1317,1339,1345,1348,1352,1374],[245,246,247,248,252,253,256],"p",{},"A pull request is the last point where a copy-paste is cheap to undo, so this is where most teams put the check. On a runner jscpd behaves like any other command: it scans the checkout, writes the reports you ask for and exits with a code the CI system reads. This page sets that up for GitHub Actions, GitLab CI and any runner that can run a shell command, and covers the three ways to decide what fails. The same check on the developer's machine is on the ",[249,250,251],"a",{"href":41},"pre-commit page","; a scheduled job that watches the trend is on the ",[249,254,255],{"href":71},"history page",".",[258,259,261],"h2",{"id":260},"choose-the-gate","Choose the gate",[263,264,265,281],"table",{},[266,267,268],"thead",{},[269,270,271,275,278],"tr",{},[272,273,274],"th",{},"Gate",[272,276,277],{},"What fails the job",[272,279,280],{},"When to use it",[282,283,284,302,323],"tbody",{},[269,285,286,293,299],{},[287,288,289],"td",{},[290,291,292],"code",{},"--threshold N",[287,294,295,296],{},"Duplication above N percent of the lines, strictly greater: 5.0 % passes ",[290,297,298],{},"--threshold 5",[287,300,301],{},"One number for the whole tree, on a small or new codebase, or in a scheduled full scan",[269,303,304,314,320],{},[287,305,306,307,310,311],{},"A committed baseline, ",[290,308,309],{},"--baseline"," with ",[290,312,313],{},"--fail-on-new-clones",[287,315,316,317],{},"A clone whose fingerprint is not in ",[290,318,319],{},".jscpd-baseline.json",[287,321,322],{},"The codebase has duplication you will not remove now and a pull request must not add more; one scan per run",[269,324,325,331,334],{},[287,326,327,328],{},"A baseline from a git ref, ",[290,329,330],{},"--baseline-from-ref origin\u002Fmain --fail-on-new-clones",[287,332,333],{},"A clone that the base branch does not have",[287,335,336],{},"The same goal with nothing to commit; the job scans twice and needs the base ref fetched",[245,338,339,340,343,344,346,347,350,351,359],{},"The gates add up: with ",[290,341,342],{},"--threshold"," and ",[290,345,313],{}," in one run, either one failing fails the job. ",[290,348,349],{},"--exit-code"," is the strictest form, exit 1 on any clone, and fits a fresh project. On ",[249,352,356],{"href":353,"rel":354},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Fmcp-demo",[355],"nofollow",[290,357,358],{},"fixtures\u002Fmcp-demo",", three exact clones make 13 % of the lines, so a threshold of 10 trips:",[361,362,368],"pre",{"className":363,"code":364,"filename":365,"language":366,"meta":367,"style":367},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","jscpd . --threshold 10 --reporters ai\n","Terminal","bash","",[290,369,370],{"__ignoreMap":367},[371,372,375,379,383,386,390,393],"span",{"class":373,"line":374},"line",1,[371,376,378],{"class":377},"sBMFI","jscpd",[371,380,382],{"class":381},"sfazB"," .",[371,384,385],{"class":381}," --threshold",[371,387,389],{"class":388},"sbssI"," 10",[371,391,392],{"class":381}," --reporters",[371,394,395],{"class":381}," ai\n",[361,397,402],{"className":398,"code":400,"language":401,"meta":367},[399],"language-text","Clones:\nsrc\u002F invoice.js:1-6 ~ print\u002Finvoice.js:1-6\nsrc\u002F invoice.js:6-12 ~ print\u002Finvoice.js:7-13\nsrc\u002F orders.js:1-12 ~ reports\u002Forders.js:1-12\n---\n3 clones · 13.0% duplication\nERROR: jscpd found too many duplicates (13.0%) over threshold (10.0%)\n","text",[290,403,400],{"__ignoreMap":367},[245,405,406,407,409,410,413],{},"The run exits 1. ",[249,408,130],{"href":131}," lists every code and what controls it; the ",[249,411,412],{"href":46},"baseline guide"," walks through both baselines with the output of each step.",[258,415,417],{"id":416},"minimal-setup","Minimal setup",[419,420,422],"h3",{"id":421},"github-actions","GitHub Actions",[361,424,429],{"className":425,"code":426,"filename":427,"language":428,"meta":367,"style":367},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: Duplication\n\non: [push, pull_request]\n\njobs:\n  jscpd:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: kucherenko\u002Fjscpd@v5\n        with:\n          threshold: 5\n",".github\u002Fworkflows\u002Fjscpd.yml","yaml",[290,430,431,444,451,475,480,489,497,508,516,530,542,550],{"__ignoreMap":367},[371,432,433,437,441],{"class":373,"line":374},[371,434,436],{"class":435},"swJcz","name",[371,438,440],{"class":439},"sMK4o",":",[371,442,443],{"class":381}," Duplication\n",[371,445,447],{"class":373,"line":446},2,[371,448,450],{"emptyLinePlaceholder":449},true,"\n",[371,452,454,458,460,463,466,469,472],{"class":373,"line":453},3,[371,455,457],{"class":456},"sfNiH","on",[371,459,440],{"class":439},[371,461,462],{"class":439}," [",[371,464,465],{"class":381},"push",[371,467,468],{"class":439},",",[371,470,471],{"class":381}," pull_request",[371,473,474],{"class":439},"]\n",[371,476,478],{"class":373,"line":477},4,[371,479,450],{"emptyLinePlaceholder":449},[371,481,483,486],{"class":373,"line":482},5,[371,484,485],{"class":435},"jobs",[371,487,488],{"class":439},":\n",[371,490,492,495],{"class":373,"line":491},6,[371,493,494],{"class":435},"  jscpd",[371,496,488],{"class":439},[371,498,500,503,505],{"class":373,"line":499},7,[371,501,502],{"class":435},"    runs-on",[371,504,440],{"class":439},[371,506,507],{"class":381}," ubuntu-latest\n",[371,509,511,514],{"class":373,"line":510},8,[371,512,513],{"class":435},"    steps",[371,515,488],{"class":439},[371,517,519,522,525,527],{"class":373,"line":518},9,[371,520,521],{"class":439},"      -",[371,523,524],{"class":435}," uses",[371,526,440],{"class":439},[371,528,529],{"class":381}," actions\u002Fcheckout@v4\n",[371,531,533,535,537,539],{"class":373,"line":532},10,[371,534,521],{"class":439},[371,536,524],{"class":435},[371,538,440],{"class":439},[371,540,541],{"class":381}," kucherenko\u002Fjscpd@v5\n",[371,543,545,548],{"class":373,"line":544},11,[371,546,547],{"class":435},"        with",[371,549,488],{"class":439},[371,551,553,556,558],{"class":373,"line":552},12,[371,554,555],{"class":435},"          threshold",[371,557,440],{"class":439},[371,559,560],{"class":388}," 5\n",[245,562,563,564,567,568,567,571,574,575,578,579,582,583,586],{},"The Action installs the release binary, through the installer on Linux and macOS runners and through npm on Windows, scans the repository with the default settings, prints the console report in the job log and fails the step when duplication is above 5 percent. Every input is a CLI flag of the same name, and the outputs (",[290,565,566],{},"duplication-percentage",", ",[290,569,570],{},"clones-found",[290,572,573],{},"exit-code"," and more) are there for the next steps of the job; the ",[249,576,577],{"href":178},"GitHub Action reference"," lists them all. ",[290,580,581],{},"@v5"," follows the latest 5.x release; pin ",[290,584,585],{},"kucherenko\u002Fjscpd@v5.4.0"," when every run must use the same binary.",[419,588,590],{"id":589},"gitlab-ci","GitLab CI",[361,592,595],{"className":425,"code":593,"filename":594,"language":428,"meta":367,"style":367},"jscpd:\n  stage: test\n  image: node:22\n  before_script:\n    - npm install -g jscpd@5\n  script:\n    - jscpd . --threshold 5 --reporters console,codeclimate,openmetrics --output report\n  artifacts:\n    reports:\n      codequality: report\u002Fgl-code-quality-report.json\n      metrics: report\u002Fjscpd-metrics.txt\n    paths:\n      - report\u002F\n",".gitlab-ci.yml",[290,596,597,603,613,623,630,638,645,652,659,666,676,686,693],{"__ignoreMap":367},[371,598,599,601],{"class":373,"line":374},[371,600,378],{"class":435},[371,602,488],{"class":439},[371,604,605,608,610],{"class":373,"line":446},[371,606,607],{"class":435},"  stage",[371,609,440],{"class":439},[371,611,612],{"class":381}," test\n",[371,614,615,618,620],{"class":373,"line":453},[371,616,617],{"class":435},"  image",[371,619,440],{"class":439},[371,621,622],{"class":381}," node:22\n",[371,624,625,628],{"class":373,"line":477},[371,626,627],{"class":435},"  before_script",[371,629,488],{"class":439},[371,631,632,635],{"class":373,"line":482},[371,633,634],{"class":439},"    -",[371,636,637],{"class":381}," npm install -g jscpd@5\n",[371,639,640,643],{"class":373,"line":491},[371,641,642],{"class":435},"  script",[371,644,488],{"class":439},[371,646,647,649],{"class":373,"line":499},[371,648,634],{"class":439},[371,650,651],{"class":381}," jscpd . --threshold 5 --reporters console,codeclimate,openmetrics --output report\n",[371,653,654,657],{"class":373,"line":510},[371,655,656],{"class":435},"  artifacts",[371,658,488],{"class":439},[371,660,661,664],{"class":373,"line":518},[371,662,663],{"class":435},"    reports",[371,665,488],{"class":439},[371,667,668,671,673],{"class":373,"line":532},[371,669,670],{"class":435},"      codequality",[371,672,440],{"class":439},[371,674,675],{"class":381}," report\u002Fgl-code-quality-report.json\n",[371,677,678,681,683],{"class":373,"line":544},[371,679,680],{"class":435},"      metrics",[371,682,440],{"class":439},[371,684,685],{"class":381}," report\u002Fjscpd-metrics.txt\n",[371,687,688,691],{"class":373,"line":552},[371,689,690],{"class":435},"    paths",[371,692,488],{"class":439},[371,694,696,698],{"class":373,"line":695},13,[371,697,521],{"class":439},[371,699,700],{"class":381}," report\u002F\n",[245,702,703,704,709,710,713,714,719,720,723,724,343,727,730,731,734],{},"The job installs jscpd from npm, fails on duplication above 5 percent and hands GitLab two files it knows how to read: the ",[249,705,706],{"href":163},[290,707,708],{},"codeclimate"," report (alias ",[290,711,712],{},"gitlab",") turns each clone into a Code Quality issue in the merge request, and the ",[249,715,716],{"href":168},[290,717,718],{},"openmetrics"," report feeds the Metrics widget with the duplication numbers and their change against the target branch (both reporters since jscpd 5.1.0). Without Node on the runner, use the release image, ",[290,721,722],{},"image: { name: \"ghcr.io\u002Fkucherenko\u002Fjscpd:5\", entrypoint: [\"\"] }","; it holds only the static binary, so ",[290,725,726],{},"--blame",[290,728,729],{},"--baseline-from-ref",", which run ",[290,732,733],{},"git",", do not work inside it.",[419,736,738],{"id":737},"any-other-runner","Any other runner",[361,740,742],{"className":363,"code":741,"filename":365,"language":366,"meta":367,"style":367},"curl -fsSL https:\u002F\u002Fjscpd.dev\u002Finstall.sh | bash\n",[290,743,744],{"__ignoreMap":367},[371,745,746,749,752,755,758],{"class":373,"line":374},[371,747,748],{"class":377},"curl",[371,750,751],{"class":381}," -fsSL",[371,753,754],{"class":381}," https:\u002F\u002Fjscpd.dev\u002Finstall.sh",[371,756,757],{"class":439}," |",[371,759,760],{"class":377}," bash\n",[361,762,764],{"className":363,"code":763,"filename":365,"language":366,"meta":367,"style":367},"jscpd . --threshold 5 --reporters console,sarif\n",[290,765,766],{"__ignoreMap":367},[371,767,768,770,772,774,777,779],{"class":373,"line":374},[371,769,378],{"class":377},[371,771,382],{"class":381},[371,773,385],{"class":381},[371,775,776],{"class":388}," 5",[371,778,392],{"class":381},[371,780,781],{"class":381}," console,sarif\n",[245,783,784,785,788,789,792,793,567,796,343,799,802,803,806],{},"The installer puts the binary into ",[290,786,787],{},"~\u002F.local\u002Fbin"," (",[290,790,791],{},"--prefix"," changes that); ",[290,794,795],{},"npm install -g jscpd@5",[290,797,798],{},"pip install jscpd",[290,800,801],{},"docker run --rm -v \"$PWD:\u002Fsrc\" ghcr.io\u002Fkucherenko\u002Fjscpd:5 ."," are the other ways in, and the ",[249,804,805],{"href":16},"installation page"," has all of them, including PowerShell for Windows. The job fails through the exit code alone, so Jenkins, CircleCI, Azure Pipelines and Buildkite need nothing else.",[258,808,810],{"id":809},"make-it-useful","Make it useful",[419,812,814],{"id":813},"upload-sarif-to-code-scanning","Upload SARIF to code scanning",[361,816,818],{"className":425,"code":817,"filename":427,"language":428,"meta":367,"style":367},"permissions:\n  contents: read\n  security-events: write\n\njobs:\n  jscpd:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: kucherenko\u002Fjscpd@v5\n        with:\n          reporters: console,sarif\n          threshold: 5\n",[290,819,820,827,837,847,851,857,863,871,877,887,897,903,912],{"__ignoreMap":367},[371,821,822,825],{"class":373,"line":374},[371,823,824],{"class":435},"permissions",[371,826,488],{"class":439},[371,828,829,832,834],{"class":373,"line":446},[371,830,831],{"class":435},"  contents",[371,833,440],{"class":439},[371,835,836],{"class":381}," read\n",[371,838,839,842,844],{"class":373,"line":453},[371,840,841],{"class":435},"  security-events",[371,843,440],{"class":439},[371,845,846],{"class":381}," write\n",[371,848,849],{"class":373,"line":477},[371,850,450],{"emptyLinePlaceholder":449},[371,852,853,855],{"class":373,"line":482},[371,854,485],{"class":435},[371,856,488],{"class":439},[371,858,859,861],{"class":373,"line":491},[371,860,494],{"class":435},[371,862,488],{"class":439},[371,864,865,867,869],{"class":373,"line":499},[371,866,502],{"class":435},[371,868,440],{"class":439},[371,870,507],{"class":381},[371,872,873,875],{"class":373,"line":510},[371,874,513],{"class":435},[371,876,488],{"class":439},[371,878,879,881,883,885],{"class":373,"line":518},[371,880,521],{"class":439},[371,882,524],{"class":435},[371,884,440],{"class":439},[371,886,529],{"class":381},[371,888,889,891,893,895],{"class":373,"line":532},[371,890,521],{"class":439},[371,892,524],{"class":435},[371,894,440],{"class":439},[371,896,541],{"class":381},[371,898,899,901],{"class":373,"line":544},[371,900,547],{"class":435},[371,902,488],{"class":439},[371,904,905,908,910],{"class":373,"line":552},[371,906,907],{"class":435},"          reporters",[371,909,440],{"class":439},[371,911,781],{"class":381},[371,913,914,916,918],{"class":373,"line":695},[371,915,555],{"class":435},[371,917,440],{"class":439},[371,919,560],{"class":388},[245,921,922,923,310,926,929,930,933,934,937,938,941,942,945,946,949,950,953,954,957,958,961,962,964,965,967,968,971],{},"The Action uploads ",[290,924,925],{},"report\u002Fjscpd-report.sarif",[290,927,928],{},"github\u002Fcodeql-action\u002Fupload-sarif"," when the file exists, so ",[290,931,932],{},"sarif"," has to be in ",[290,935,936],{},"reporters"," (the default, ",[290,939,940],{},"console",", writes no file) and the job needs ",[290,943,944],{},"security-events: write","; the upload step runs with ",[290,947,948],{},"continue-on-error",", so a failed upload shows in its log and does not fail the job. Each clone becomes an alert with the counterpart location linked. Against a baseline, new clones arrive as ",[290,951,952],{},"error"," and the rest as ",[290,955,956],{},"warning",", and ",[290,959,960],{},"--sarif-error-tokens N"," raises large clones to ",[290,963,952],{}," as well; the ",[249,966,157],{"href":158}," page has the rule ids. Outside the Action, run the same reporter and call ",[290,969,970],{},"upload-sarif"," yourself.",[419,973,975],{"id":974},"fetch-enough-history","Fetch enough history",[361,977,979],{"className":425,"code":978,"filename":427,"language":428,"meta":367,"style":367},"- uses: actions\u002Fcheckout@v4\n  with:\n    fetch-depth: 0\n- uses: kucherenko\u002Fjscpd@v5\n  with:\n    baseline-from-ref: origin\u002F${{ github.base_ref }}\n    fail-on-new-clones: true\n",[290,980,981,992,999,1009,1019,1025,1035],{"__ignoreMap":367},[371,982,983,986,988,990],{"class":373,"line":374},[371,984,985],{"class":439},"-",[371,987,524],{"class":435},[371,989,440],{"class":439},[371,991,529],{"class":381},[371,993,994,997],{"class":373,"line":446},[371,995,996],{"class":435},"  with",[371,998,488],{"class":439},[371,1000,1001,1004,1006],{"class":373,"line":453},[371,1002,1003],{"class":435},"    fetch-depth",[371,1005,440],{"class":439},[371,1007,1008],{"class":388}," 0\n",[371,1010,1011,1013,1015,1017],{"class":373,"line":477},[371,1012,985],{"class":439},[371,1014,524],{"class":435},[371,1016,440],{"class":439},[371,1018,541],{"class":381},[371,1020,1021,1023],{"class":373,"line":482},[371,1022,996],{"class":435},[371,1024,488],{"class":439},[371,1026,1027,1030,1032],{"class":373,"line":491},[371,1028,1029],{"class":435},"    baseline-from-ref",[371,1031,440],{"class":439},[371,1033,1034],{"class":381}," origin\u002F${{ github.base_ref }}\n",[371,1036,1037,1040,1042],{"class":373,"line":499},[371,1038,1039],{"class":435},"    fail-on-new-clones",[371,1041,440],{"class":439},[371,1043,1044],{"class":456}," true\n",[245,1046,1047,1050,1051,1053,1054,1057,1058,1061,1062,1065,1066,1069,1070,1073,1074,256],{},[290,1048,1049],{},"actions\u002Fcheckout"," fetches one commit by default. ",[290,1052,729],{}," needs the base branch's tree and ",[290,1055,1056],{},"--history"," every commit of its range, so give both ",[290,1059,1060],{},"fetch-depth: 0","; ",[290,1063,1064],{},"github.base_ref"," is the target branch of the pull request. On GitLab, set ",[290,1067,1068],{},"GIT_DEPTH: \"0\""," in the job's ",[290,1071,1072],{},"variables",", or fetch the one branch you need and point jscpd at it: ",[290,1075,1076],{},"git fetch origin main && jscpd . --baseline-from-ref FETCH_HEAD --fail-on-new-clones",[419,1078,1080],{"id":1079},"keep-the-run-short","Keep the run short",[361,1082,1084],{"className":425,"code":1083,"filename":427,"language":428,"meta":367,"style":367},"- uses: kucherenko\u002Fjscpd@v5\n  with:\n    format: typescript,tsx\n    ignore: \"**\u002Fgenerated\u002F**,**\u002F__snapshots__\u002F**\"\n    version: \"5.4.0\"\n",[290,1085,1086,1096,1102,1112,1128],{"__ignoreMap":367},[371,1087,1088,1090,1092,1094],{"class":373,"line":374},[371,1089,985],{"class":439},[371,1091,524],{"class":435},[371,1093,440],{"class":439},[371,1095,541],{"class":381},[371,1097,1098,1100],{"class":373,"line":446},[371,1099,996],{"class":435},[371,1101,488],{"class":439},[371,1103,1104,1107,1109],{"class":373,"line":453},[371,1105,1106],{"class":435},"    format",[371,1108,440],{"class":439},[371,1110,1111],{"class":381}," typescript,tsx\n",[371,1113,1114,1117,1119,1122,1125],{"class":373,"line":477},[371,1115,1116],{"class":435},"    ignore",[371,1118,440],{"class":439},[371,1120,1121],{"class":439}," \"",[371,1123,1124],{"class":381},"**\u002Fgenerated\u002F**,**\u002F__snapshots__\u002F**",[371,1126,1127],{"class":439},"\"\n",[371,1129,1130,1133,1135,1137,1140],{"class":373,"line":482},[371,1131,1132],{"class":435},"    version",[371,1134,440],{"class":439},[371,1136,1121],{"class":439},[371,1138,1139],{"class":381},"5.4.0",[371,1141,1127],{"class":439},[245,1143,1144,1145,1148,1149,1152,1153,1156,1157,1160,1161,1164,1165,1168,1169,1172,1173,1176,1177,1180,1181,1184],{},"jscpd scans a large repository in seconds, so most of a job's time is the checkout and the install. ",[290,1146,1147],{},"format"," limits the run to the languages you gate on, ",[290,1150,1151],{},"ignore"," keeps generated code out (inside a git checkout jscpd already skips what ",[290,1154,1155],{},".gitignore"," lists), and ",[290,1158,1159],{},"--workers"," sets the thread count, which is automatic by default. ",[290,1162,1163],{},"version"," installs the same release on every run, and ",[290,1166,1167],{},"skip-install: true"," uses a binary your image already has. A semantic run is the one slow start: ",[290,1170,1171],{},"--semantic"," needs the 548 MB model, so keep the jscpd cache directory between runs, ",[290,1174,1175],{},"~\u002F.cache\u002Fjscpd"," on a Linux runner with ",[290,1178,1179],{},"actions\u002Fcache","; the ",[249,1182,1183],{"href":91},"semantic clones guide"," has the cache step and the directories of the other systems.",[258,1186,115],{"id":1187},"reference",[1189,1190,1191,1200,1205,1210],"ul",{},[1192,1193,1194,1196,1197,256],"li",{},[249,1195,177],{"href":178},", every input and output, generated from ",[290,1198,1199],{},"action.yml",[1192,1201,1202,1204],{},[249,1203,120],{"href":121},", every flag with its config key and default.",[1192,1206,1207,1209],{},[249,1208,130],{"href":131},", what makes a run exit 0, 1 or 2.",[1192,1211,1212,1214],{},[249,1213,135],{"href":136},", the file each one writes.",[258,1216,1218],{"id":1217},"troubleshooting","Troubleshooting",[419,1220,1222],{"id":1221},"the-job-is-green-although-jscpd-printed-an-error","The job is green although jscpd printed an error",[245,1224,1225,1226,1229,1230,1233,1234,1237,1238,1241,1242,1245,1246,1249,1250,1253,1254,343,1257,1260],{},"A pipe hides the exit code: ",[290,1227,1228],{},"jscpd . | tee jscpd.log"," returns the code of ",[290,1231,1232],{},"tee",". Put ",[290,1235,1236],{},"set -o pipefail"," at the top of the script, or write the log with a reporter (",[290,1239,1240],{},"--reporters console,json",") and drop the pipe. GitHub Actions turns ",[290,1243,1244],{},"pipefail"," on only when the step sets ",[290,1247,1248],{},"shell: bash"," explicitly; the default shell runs ",[290,1251,1252],{},"bash -e {0}"," without it. ",[290,1255,1256],{},"|| true",[290,1258,1259],{},"continue-on-error: true"," hide the code as well.",[419,1262,1264],{"id":1263},"git-ref-originmain-not-found",[290,1265,1266],{},"git ref 'origin\u002Fmain' not found",[245,1268,1269,1270,1272,1273,1275,1276,1278,1279,1282,1283,1285],{},"The checkout is shallow, so the ref that ",[290,1271,729],{}," names is not there; jscpd exits 1 and its message says to fetch it first. Set ",[290,1274,1060],{}," on ",[290,1277,1049],{},", or fetch the branch in the step before and use ",[290,1280,1281],{},"FETCH_HEAD",". ",[290,1284,1056],{}," needs the commits of its range present for the same reason.",[419,1287,1289],{"id":1288},"windows-runners","Windows runners",[245,1291,1292,1293,1295,1296,1298],{},"The Action's Windows step installs with npm, so the runner needs Node.js; GitHub-hosted Windows images have it. If you run the curl installer yourself, make sure ",[290,1294,366],{}," is Git Bash: when it resolves to WSL, the installer sees Linux, installs a Linux binary inside the WSL filesystem, and the PowerShell steps never find it. The PowerShell installer on the ",[249,1297,805],{"href":16}," is the native route.",[419,1300,1302],{"id":1301},"every-run-downloads-the-semantic-model","Every run downloads the semantic model",[245,1304,1305,1306,1308,1309,1312],{},"The model lives in the jscpd cache directory, which a fresh runner does not have. Restore the directory with ",[290,1307,1179],{}," before the scan; with the model in place, ",[290,1310,1311],{},"--semantic-download"," has nothing to fetch, and the vectors of unchanged functions come from the same cache.",[419,1314,1316],{"id":1315},"a-green-job-that-scanned-nothing","A green job that scanned nothing",[245,1318,1319,1320,1323,1324,1327,1328,1331,1332,788,1335,1338],{},"A wrong path, a ",[290,1321,1322],{},"--format"," that matches no file or an ",[290,1325,1326],{},"--ignore"," that swallows everything leaves an empty report and exit 0, with ",[290,1329,1330],{},"Warning: jscpd analyzed no files: check the paths and the --format, --ignore and --pattern filters"," in the log. ",[290,1333,1334],{},"--fail-on-empty",[290,1336,1337],{},"fail-on-empty: true"," in the Action, jscpd 5.2.1+) turns that into exit 1:",[361,1340,1343],{"className":1341,"code":1342,"language":401,"meta":367},[399],"ERROR: jscpd analyzed no files (--fail-on-empty): check the paths and the --format, --ignore and --pattern filters\n",[290,1344,1342],{"__ignoreMap":367},[245,1346,1347],{},"A path that does not exist, an unknown format name and a reporter that cannot write its file exit 1 on their own since jscpd 5.2.1.",[258,1349,1351],{"id":1350},"related","Related",[1189,1353,1354,1359,1364,1369],{},[1192,1355,1356,1358],{},[249,1357,40],{"href":41}," for the same check on the developer's machine.",[1192,1360,1361,1363],{},[249,1362,45],{"href":46}," for both baselines in detail, with the output of each step.",[1192,1365,1366,1368],{},[249,1367,70],{"href":71}," for a scheduled job that shows where the number is heading.",[1192,1370,1371,1373],{},[249,1372,130],{"href":131}," for every code and the flag behind it.",[1375,1376,1377],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":367,"searchDepth":446,"depth":446,"links":1379},[1380,1381,1386,1391,1392,1399],{"id":260,"depth":446,"text":261},{"id":416,"depth":446,"text":417,"children":1382},[1383,1384,1385],{"id":421,"depth":453,"text":422},{"id":589,"depth":453,"text":590},{"id":737,"depth":453,"text":738},{"id":809,"depth":446,"text":810,"children":1387},[1388,1389,1390],{"id":813,"depth":453,"text":814},{"id":974,"depth":453,"text":975},{"id":1079,"depth":453,"text":1080},{"id":1187,"depth":446,"text":115},{"id":1217,"depth":446,"text":1218,"children":1393},[1394,1395,1396,1397,1398],{"id":1221,"depth":453,"text":1222},{"id":1263,"depth":453,"text":1266},{"id":1288,"depth":453,"text":1289},{"id":1301,"depth":453,"text":1302},{"id":1315,"depth":453,"text":1316},{"id":1350,"depth":446,"text":1351},"Run jscpd as a failing check on GitHub Actions, GitLab CI or any other runner, with SARIF, Code Quality and metrics reports.","md",null,{},{"icon":38},{"title":35,"description":1406},"Add jscpd to GitHub Actions, GitLab CI or any runner as a check that fails on duplication, upload SARIF to code scanning and feed the GitLab merge-request widgets.","JGNlymr-tGdsu1_gs_Ou61Cc0Ioe65HwslwUIalz-s0",[1409,1411],{"title":25,"path":26,"stem":27,"description":1410,"icon":28,"children":-1},"The config file jscpd reads, the handful of keys most projects set, and how to check what a run will use.",{"title":40,"path":41,"stem":42,"description":1412,"icon":43,"children":-1},"Run jscpd at git commit with the pre-commit framework, Husky or a plain git hook, so a copy-paste is caught before it leaves the machine.",1791302980745]