Merge pull request #1048 from kucherenko/docs/language-aware #579
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Rust | |
| on: | |
| push: | |
| branches: [main, master, "heist/*"] | |
| pull_request: | |
| branches: [main, master, "heist/*"] | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| permissions: | |
| contents: read | |
| checks: write # dorny/test-reporter posts check runs | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable | |
| with: | |
| toolchain: "1.97.1" | |
| components: clippy, rustfmt | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 | |
| with: | |
| workspaces: "rust -> rust/target" | |
| cache-on-failure: true | |
| - name: Install cargo-nextest | |
| uses: taiki-e/install-action@d438492cf8a250514fa2d34b30bc3c0dc37c65ff # v2.87.8 | |
| with: | |
| tool: nextest | |
| - name: Build | |
| working-directory: rust | |
| run: cargo build --workspace | |
| - name: Test | |
| working-directory: rust | |
| run: cargo nextest run --workspace --profile ci | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: nextest-results-${{ matrix.os }} | |
| path: rust/target/nextest/ci/nextest.xml | |
| if-no-files-found: warn | |
| - name: Test Report | |
| if: always() | |
| continue-on-error: true | |
| uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0 | |
| with: | |
| name: Rust Tests (${{ matrix.os }}) | |
| path: rust/target/nextest/ci/nextest.xml | |
| reporter: java-junit | |
| fail-on-error: false | |
| - name: Clippy | |
| working-directory: rust | |
| run: cargo clippy --workspace -- -D warnings | |
| - name: Format check | |
| working-directory: rust | |
| run: cargo fmt --check | |
| # Bash-only steps below run on Linux; Windows defaults to pwsh. | |
| - name: Tokenizer purity check | |
| if: runner.os == 'Linux' | |
| run: | | |
| cd rust | |
| TOKENIZER_DEPS=$(cargo metadata --format-version 1 | \ | |
| python3 -c " | |
| import sys, json | |
| meta = json.load(sys.stdin) | |
| tokenizer_id = next(p['id'] for p in meta['packages'] if p['name'] == 'cpd-tokenizer') | |
| dep_names = set() | |
| queue = [tokenizer_id] | |
| id_to_pkg = {p['id']: p for p in meta['packages']} | |
| resolve_map = {n['id']: [d['pkg'] for d in n['deps']] for n in meta['resolve']['nodes']} | |
| visited = set() | |
| while queue: | |
| pid = queue.pop() | |
| if pid in visited: continue | |
| visited.add(pid) | |
| if pid in id_to_pkg: | |
| dep_names.add(id_to_pkg[pid]['name']) | |
| for dep_id in resolve_map.get(pid, []): | |
| queue.append(dep_id) | |
| print('\n'.join(dep_names)) | |
| ") | |
| echo "cpd-tokenizer transitive deps: $TOKENIZER_DEPS" | |
| for crate in tokio async-std walkdir ignore jwalk; do | |
| if echo "$TOKENIZER_DEPS" | grep -q "^$crate$"; then | |
| echo "ERROR: cpd-tokenizer depends on I/O crate: $crate" | |
| exit 1 | |
| fi | |
| done | |
| echo "Tokenizer purity check: PASS" | |
| - name: publish=false check | |
| if: runner.os == 'Linux' | |
| run: | | |
| cd rust | |
| for toml in crates/*/Cargo.toml; do | |
| if ! grep -q 'publish = false' "$toml"; then | |
| echo "ERROR: $toml missing 'publish = false'" | |
| exit 1 | |
| fi | |
| done | |
| echo "publish=false check: PASS" | |
| - name: cargo-deny | |
| if: matrix.os == 'ubuntu-latest' | |
| working-directory: rust | |
| run: cargo install cargo-deny --locked && cargo deny check | |
| msrv: | |
| name: MSRV | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Read declared MSRV | |
| id: msrv | |
| run: | | |
| version=$(sed -nE 's/^rust-version = "([0-9.]+)"$/\1/p' rust/Cargo.toml | head -1) | |
| if [ -z "$version" ]; then | |
| echo "ERROR: no rust-version found in rust/Cargo.toml" | |
| exit 1 | |
| fi | |
| echo "Declared MSRV: $version" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable | |
| with: | |
| toolchain: ${{ steps.msrv.outputs.version }} | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 | |
| with: | |
| workspaces: "rust -> rust/target" | |
| key: msrv-${{ steps.msrv.outputs.version }} | |
| cache-on-failure: true | |
| # `+toolchain` outranks rust-toolchain.toml, which pins a newer compiler | |
| # for day-to-day development. Without it this job would silently test the | |
| # pinned version instead of the MSRV. | |
| - name: cargo check at MSRV | |
| working-directory: rust | |
| run: cargo +${{ steps.msrv.outputs.version }} check --workspace --all-targets --locked | |
| # End-to-end run of the release binary over the multi-format corpus in | |
| # fixtures/ (550 files across 130+ formats). Unit tests cover the parts; | |
| # this catches a reporter or walker regression that only shows on the | |
| # real corpus. | |
| smoke: | |
| name: smoke test (fixtures corpus) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable | |
| with: | |
| toolchain: "1.97.1" | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 | |
| with: | |
| workspaces: "rust -> rust/target" | |
| key: smoke-release | |
| cache-on-failure: true | |
| - name: Build release binary | |
| working-directory: rust | |
| run: cargo build --release -p jscpd --locked | |
| - name: Scan fixtures (console + json) | |
| working-directory: rust | |
| run: ./target/release/jscpd ../fixtures --reporters console,json --output ../smoke-report --min-tokens 50 | |
| - name: Assert JSON report | |
| run: | | |
| report=smoke-report/jscpd-report.json | |
| test -s "$report" | |
| clones=$(jq '.statistics.total.clones' "$report") | |
| sources=$(jq '.statistics.total.sources' "$report") | |
| formats=$(jq '.statistics.formats | keys | length' "$report") | |
| duplicates=$(jq '.duplicates | length' "$report") | |
| echo "clones=$clones sources=$sources formats=$formats duplicates=$duplicates" | |
| [ "$clones" -gt 0 ] || { echo "expected statistics.total.clones > 0"; exit 1; } | |
| [ "$duplicates" -eq "$clones" ] || { echo "duplicates[] length ($duplicates) != statistics.total.clones ($clones)"; exit 1; } | |
| [ "$sources" -ge 300 ] || { echo "expected at least 300 scanned files, got $sources"; exit 1; } | |
| [ "$formats" -ge 100 ] || { echo "expected at least 100 detected formats, got $formats"; exit 1; } | |
| - name: SARIF reporter | |
| working-directory: rust | |
| run: | | |
| ./target/release/jscpd ../fixtures --reporters sarif,silent --output ../smoke-report --min-tokens 50 | |
| jq -e '.runs[0].results | length > 0' ../smoke-report/jscpd-report.sarif | |
| - name: HTML reporter | |
| working-directory: rust | |
| run: | | |
| ./target/release/jscpd ../fixtures --reporters html,silent --output ../smoke-report --min-tokens 50 | |
| test -s ../smoke-report/jscpd-report.html |