From e300353440c02757a7876fdc47c79f8800e69d59 Mon Sep 17 00:00:00 2001 From: Hweinstock <42325418+Hweinstock@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:40:16 -0500 Subject: [PATCH] build(jscpd): validate branch name before running CI (#6124) ## Problem ... ## Solution - (title) - Refactor github actions such that JSCPD requires `lint-commits`. - Add step to `lint-commits` that verifies branch name according to rules here: https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names#naming-branches-and-tags - Note this means that no CI tasks will run on PRs with branches that don't fit naming conventions. --- License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .github/workflows/copyPasteDetection.yml | 86 ------------------------ .github/workflows/lintbranch.js | 67 ++++++++++++++++++ .github/workflows/node.js.yml | 86 ++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 86 deletions(-) delete mode 100644 .github/workflows/copyPasteDetection.yml create mode 100644 .github/workflows/lintbranch.js diff --git a/.github/workflows/copyPasteDetection.yml b/.github/workflows/copyPasteDetection.yml deleted file mode 100644 index ad78e409fca..00000000000 --- a/.github/workflows/copyPasteDetection.yml +++ /dev/null @@ -1,86 +0,0 @@ -# # github actions: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs -# # setup-node: https://github.com/actions/setup-node - -# name: Copy-Paste Detection - -# on: -# pull_request: -# branches: [master, feature/*, staging] - -# jobs: -# jscpd: -# runs-on: ubuntu-latest -# strategy: -# matrix: -# node-version: [18.x] -# env: -# NODE_OPTIONS: '--max-old-space-size=8192' - -# steps: -# - uses: actions/checkout@v4 -# with: -# fetch-depth: 0 - -# - name: Use Node.js ${{ matrix.node-version }} -# uses: actions/setup-node@v4 -# with: -# node-version: ${{ matrix.node-version }} - -# - name: Fetch fork upstream -# 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 - -# - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" - -# - if: always() -# 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 diff --git a/.github/workflows/lintbranch.js b/.github/workflows/lintbranch.js new file mode 100644 index 00000000000..05fc677dac5 --- /dev/null +++ b/.github/workflows/lintbranch.js @@ -0,0 +1,67 @@ +// Check that branch name conforms to GitHub naming convention: +// https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names#naming-branches-and-tags + +// To run self-tests, +// node lintbranch.js test +// TODO: deduplicate code from lintbranch.js and lintcommit.js. + +function isValid(branchName) { + const branchNameRegex = /^[a-zA-Z][a-zA-Z0-9._/-]*$/ + + return branchNameRegex.test(branchName) +} + +function run(branchName) { + if (isValid(branchName)) { + console.log(`Branch name "${branchName}" is valid.`) + process.exit(0) + } else { + const helpUrl = + 'https://docs.github.com/en/get-started/using-git/dealing-with-special-characters-in-branch-and-tag-names#naming-branches-and-tags' + console.log(`Branch name "${branchName}" is invalid see ${helpUrl} for more information.`) + process.exit(1) + } +} + +function _test() { + const tests = { + 'feature/branch-name': true, + feature_123: true, + 'my-branch': true, + '123invalid-start': false, + '!invalid@start': false, + '': false, + 'another/valid-name134': true, + 'feature/123";id;{echo,Y2F0IC9ldGMvcGFzc3dk}|{base64,-d}|{bash,-i};#': false, + } + + let passed = 0 + let failed = 0 + + for (const [branchName, expected] of Object.entries(tests)) { + const result = isValid(branchName) + if (result === expected) { + console.log(`✅ Test passed for "${branchName}"`) + passed++ + } else { + console.log(`❌ Test failed for "${branchName}" (expected "${expected}", got "${result}")`) + failed++ + } + } + + console.log(`\n${passed} tests passed, ${failed} tests failed`) +} + +function main() { + const mode = process.argv[2] + + if (mode === 'test') { + _test() + } else if (mode === 'run') { + run(process.argv[3]) + } else { + throw new Error(`Unknown mode: ${mode}`) + } +} + +main() diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 7a8f5c4323e..d42b6ef76ee 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -32,6 +32,11 @@ jobs: - uses: actions/setup-node@v4 with: node-version: '20' + - name: Check Branch title + env: + BRANCH_NAME: ${{ github.head_ref }} + run: | + node "$GITHUB_WORKSPACE/.github/workflows/lintbranch.js" run "$BRANCH_NAME" - name: Check PR title run: | node "$GITHUB_WORKSPACE/.github/workflows/lintcommit.js" @@ -55,6 +60,87 @@ jobs: - run: npm run testCompile - run: npm run lint + jscpd: + needs: lint-commits + if: ${{ github.event_name == 'pull_request'}} + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + env: + NODE_OPTIONS: '--max-old-space-size=8192' + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Fetch fork upstream + env: + REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }} + run: | + git remote add forkUpstream https://github.com/$REPO_NAME # URL of the fork + git fetch forkUpstream # Fetch fork + + - name: Compute git diff + env: + CURRENT_BRANCH: ${{ github.head_ref }} + TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} + run: git diff --name-only origin/$TARGET_BRANCH forkUpstream/$CURRENT_BRANCH > diff_output.txt + + - run: npm install -g jscpd + + - run: jscpd --config "$GITHUB_WORKSPACE/.github/workflows/jscpd.json" + + - if: always() + 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 + macos: needs: lint-commits name: test macOS