| diff --git a/.github/workflows/copyPasteDetection.yml b/.github/workflows/copyPasteDetection.yml |
| index 793337de5..746b3cecd 100644 |
| --- a/.github/workflows/copyPasteDetection.yml |
| +++ b/.github/workflows/copyPasteDetection.yml |
| @@ -26,61 +26,47 @@ jobs: |
| with: |
| node-version: ${{ matrix.node-version }} |
| |
| + - name: Determine if local |
| + run: echo "IS_LOCAL=false" >> $GITHUB_ENV |
| + |
| - name: Fetch fork upstream |
| + if: ${{ env.IS_LOCAL == 'false' }} |
| run: | |
| git remote add forkUpstream https://github.com/${{ github.event.pull_request.head.repo.full_name }} # URL of the fork |
| git fetch forkUpstream # Fetch fork |
| |
| - name: Determine base and target branches for comparison. |
| run: | |
| - echo "CURRENT_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV |
| - echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV |
| - - run: git diff --name-only origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt |
| - - run: | |
| - npm install -g jscpd |
| + if [[ $IS_LOCAL == 'false' ]]; then |
| + echo "CURRENT_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV |
| + echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV |
| + else |
| + echo "CURRENT_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV |
| + echo "TARGET_BRANCH=master" >> $GITHUB_ENV |
| + fi |
| + |
| + - name: Print base and target branches for comparison. |
| + run: | |
| + echo "CURRENT_BRANCH=$CURRENT_BRANCH" |
| + echo "TARGET_BRANCH=$TARGET_BRANCH" |
| + |
| + - name: Compare target and current branches. |
| + run: | |
| + if [[ $IS_LOCAL == 'false' ]]; then |
| + git diff origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt |
| + else |
| + git diff origin/$TARGET_BRANCH $CURRENT_BRANCH > diff_output.txt |
| + fi |
| + |
| + - run: npm install -g jscpd |
| |
| - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" |
| |
| - - if: always() |
| + - if: ${{ env.IS_LOCAL == 'false' }} |
| uses: actions/upload-artifact@v4 |
| with: |
| name: unfiltered-jscpd-report |
| path: ./jscpd-report.json |
| |
| - - name: Filter jscpd report for changed files |
| - run: | |
| - if [ ! -f ./jscpd-report.json ]; then |
| - echo "jscpd-report.json not found" |
| - exit 1 |
| - fi |
| - echo "Filtering jscpd report for changed files..." |
| - CHANGED_FILES=$(jq -R -s -c 'split("\n")[:-1]' diff_output.txt) |
| - echo "Changed files: $CHANGED_FILES" |
| - jq --argjson changed_files "$CHANGED_FILES" ' |
| - .duplicates | map(select( |
| - (.firstFile?.name as $fname | $changed_files | any(. == $fname)) or |
| - (.secondFile?.name as $sname | $changed_files | any(. == $sname)) |
| - )) |
| - ' ./jscpd-report.json > filtered-jscpd-report.json |
| - cat filtered-jscpd-report.json |
| - |
| - name: Check for duplicates |
| - run: | |
| - if [ $(wc -l < ./filtered-jscpd-report.json) -gt 1 ]; then |
| - echo "filtered_report_exists=true" >> $GITHUB_ENV |
| - else |
| - echo "filtered_report_exists=false" >> $GITHUB_ENV |
| - fi |
| - - name: upload filtered report (if applicable) |
| - if: env.filtered_report_exists == 'true' |
| - uses: actions/upload-artifact@v4 |
| - with: |
| - name: filtered-jscpd-report |
| - path: ./filtered-jscpd-report.json |
| - |
| - - name: Fail and log found duplicates. |
| - if: env.filtered_report_exists == 'true' |
| - run: | |
| - cat ./filtered-jscpd-report.json |
| - echo "Duplications found, failing the check." |
| - exit 1 |
| + run: node "$GITHUB_WORKSPACE/.github/workflows/filterDuplicates.js" diff_output.txt jscpd-report.json |
| diff --git a/.github/workflows/filterDuplicates.js b/.github/workflows/filterDuplicates.js |
| new file mode 100644 |
| index 000000000..b2f1e913e |
| --- /dev/null |
| +++ b/.github/workflows/filterDuplicates.js |
| @@ -0,0 +1,85 @@ |
| +const fs = require('fs/promises') |
| + |
| +function parseDiffFilePath(filePathLine) { |
| + return filePathLine.split(' ')[2].split('/').slice(1).join('/') |
| +} |
| + |
| +function parseDiffRange(rangeLine) { |
| + const [_fromRange, toRange] = rangeLine.split(' ').slice(1, 3) |
| + const [startLine, numLines] = toRange.slice(1).split(',').map(Number) |
| + const range = [startLine, startLine + numLines] |
| + return range |
| +} |
| + |
| +async function parseDiff(diffPath) { |
| + const diff = await fs.readFile(diffPath, 'utf8') |
| + const lines = diff.split('\n') |
| + let currentFile = null |
| + let currentFileChanges = [] |
| + const fileChanges = new Map() |
| + |
| + for (const line of lines) { |
| + if (line.startsWith('diff')) { |
| + if (currentFile) { |
| + fileChanges.set(currentFile, currentFileChanges) |
| + } |
| + currentFile = parseDiffFilePath(line) |
| + currentFileChanges = [] |
| + } |
| + if (line.startsWith('@@')) { |
| + currentFileChanges.push(parseDiffRange(line)) |
| + } |
| + } |
| + |
| + return fileChanges |
| +} |
| + |
| +function doesOverlap(range1, range2) { |
| + const [start1, end1] = range1 |
| + const [start2, end2] = range2 |
| + return (start1 >= start2 && start1 <= end2) || (end1 >= start2 && end1 <= end2) |
| +} |
| + |
| +function isCloneInChanges(changes, cloneInstance) { |
| + const fileName = cloneInstance.name |
| + const cloneStart = cloneInstance.start |
| + const cloneEnd = cloneInstance.end |
| + const lineChangeRanges = changes.get(fileName) |
| + |
| + if (!lineChangeRanges) { |
| + return false |
| + } |
| + |
| + return lineChangeRanges.some((range) => doesOverlap([cloneStart, cloneEnd], range)) |
| +} |
| + |
| +function isInChanges(changes, dupe) { |
| + return isCloneInChanges(changes, dupe.firstFile) || isCloneInChanges(changes, dupe.secondFile) |
| +} |
| + |
| +function filterDuplicates(report, changes) { |
| + duplicates = [] |
| + for (const dupe of report.duplicates) { |
| + if (isInChanges(changes, dupe)) { |
| + duplicates.push(dupe) |
| + } |
| + } |
| + return duplicates |
| +} |
| + |
| +async function main() { |
| + const rawDiffPath = process.argv[2] |
| + const jscpdReportPath = process.argv[3] |
| + const changes = await parseDiff(rawDiffPath) |
| + const jscpdReport = JSON.parse(await fs.readFile(jscpdReportPath, 'utf8')) |
| + const filteredDuplicates = filterDuplicates(jscpdReport, changes) |
| + |
| + console.log(filteredDuplicates) |
| + console.log('%s files changes', changes.size) |
| + console.log('%s duplicates found', filteredDuplicates.length) |
| + if (filteredDuplicates.length > 0) { |
| + process.exit(1) |
| + } |
| +} |
| + |
| +void main() |
|
|