mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 647db30e2b |
@@ -0,0 +1,23 @@
|
||||
os: Visual Studio 2015
|
||||
|
||||
version: "{build}"
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- channel: stable
|
||||
target: x86_64-pc-windows-msvc
|
||||
- channel: nightly
|
||||
target: x86_64-pc-windows-msvc
|
||||
|
||||
install:
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init -yv --default-toolchain %channel% --default-host %target%
|
||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
- rustc -vV && cargo -vV
|
||||
|
||||
build_script:
|
||||
- cargo build --verbose
|
||||
|
||||
test_script:
|
||||
- cargo test --verbose
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: Security audit
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: rustsec/audit-check@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+78
-49
@@ -2,9 +2,6 @@ on: [push, pull_request]
|
||||
|
||||
name: Basic CI
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: cargo check
|
||||
@@ -13,9 +10,15 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
|
||||
# For bindgen: https://github.com/rust-lang/rust-bindgen/issues/1797
|
||||
- uses: KyleMayes/install-llvm-action@v2
|
||||
- uses: KyleMayes/install-llvm-action@v1
|
||||
if: matrix.os == 'windows-latest'
|
||||
with:
|
||||
version: "11.0"
|
||||
@@ -25,7 +28,7 @@ jobs:
|
||||
|
||||
- name: Check
|
||||
run: |
|
||||
cargo check --all --all-features
|
||||
cargo check
|
||||
|
||||
test:
|
||||
name: cargo test
|
||||
@@ -34,9 +37,15 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
|
||||
# For bindgen: https://github.com/rust-lang/rust-bindgen/issues/1797
|
||||
- uses: KyleMayes/install-llvm-action@v2
|
||||
- uses: KyleMayes/install-llvm-action@v1
|
||||
if: matrix.os == 'windows-latest'
|
||||
with:
|
||||
version: "11.0"
|
||||
@@ -44,15 +53,22 @@ jobs:
|
||||
- run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Test
|
||||
- name: Check
|
||||
run: |
|
||||
cargo test
|
||||
cargo check
|
||||
|
||||
fmt:
|
||||
name: cargo fmt --all -- --check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
|
||||
- run: rustup component add rustfmt
|
||||
- name: cargo fmt
|
||||
run: |
|
||||
cargo fmt --all -- --check
|
||||
@@ -61,32 +77,16 @@ jobs:
|
||||
name: cargo clippy -- -D warnings
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: |
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
|
||||
wasm:
|
||||
name: cargo build (wasm32-wasip1)
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
WASI_SDK_VERSION: "33"
|
||||
WASI_SDK_PATH: ${{ github.workspace }}/wasi-sdk
|
||||
# onig_sys compiles bundled C, so it needs a C compiler with a WASI
|
||||
# sysroot to cross-compile to wasm.
|
||||
CC_wasm32_wasip1: ${{ github.workspace }}/wasi-sdk/bin/clang
|
||||
AR_wasm32_wasip1: ${{ github.workspace }}/wasi-sdk/bin/llvm-ar
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install wasm target
|
||||
run: rustup target add wasm32-wasip1
|
||||
- name: Install WASI SDK
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
curl -sSfL "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz" | tar xz
|
||||
mv "wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux" "${WASI_SDK_PATH}"
|
||||
- name: Build
|
||||
run: cargo build --target wasm32-wasip1
|
||||
- name: Clippy
|
||||
run: cargo clippy --target wasm32-wasip1 -- -D warnings
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
- run: rustup component add clippy
|
||||
- name: cargo clippy
|
||||
run: |
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
grcov:
|
||||
name: Code coverage
|
||||
@@ -95,29 +95,58 @@ jobs:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
toolchain:
|
||||
- nightly
|
||||
cargo_flags:
|
||||
- "--all-features"
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v6
|
||||
- name: Install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@cargo-llvm-cov
|
||||
- name: Generate code coverage
|
||||
env:
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
run: |
|
||||
cargo llvm-cov --all-features --lcov --branch --output-path lcov.info
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install nightly --no-self-update -c rustfmt --profile minimal
|
||||
rustup default nightly
|
||||
|
||||
- name: "`grcov` ~ install"
|
||||
run: cargo install grcov
|
||||
|
||||
- name: cargo test
|
||||
run: |
|
||||
cargo test --all --no-fail-fast ${{ matrix.cargo_flags }}
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off'
|
||||
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off'
|
||||
|
||||
- name: Generate coverage data
|
||||
id: grcov
|
||||
run: |
|
||||
grcov target/debug/ \
|
||||
--branch \
|
||||
--llvm \
|
||||
--source-dir . \
|
||||
--output-path lcov.info \
|
||||
--ignore-not-existing \
|
||||
--excl-line "#\\[derive\\(" \
|
||||
--excl-br-line "#\\[derive\\(" \
|
||||
--excl-start "#\\[cfg\\(test\\)\\]" \
|
||||
--excl-br-start "#\\[cfg\\(test\\)\\]" \
|
||||
--commit-sha ${{ github.sha }} \
|
||||
--service-job-id ${{ github.job }} \
|
||||
--service-name "GitHub Actions" \
|
||||
--service-number ${{ github.run_id }}
|
||||
- name: Upload coverage as artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: lcov.info
|
||||
# path: ${{ steps.grcov.outputs.report }}
|
||||
path: lcov.info
|
||||
|
||||
- name: Upload coverage to codecov.io
|
||||
uses: codecov/codecov-action@v7
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: lcov.info
|
||||
# file: ${{ steps.grcov.outputs.report }}
|
||||
file: lcov.info
|
||||
fail_ci_if_error: true
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
name: CodSpeed
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
pull_request:
|
||||
# `workflow_dispatch` allows CodSpeed to trigger backtest
|
||||
# performance analysis in order to generate initial data.
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
codspeed:
|
||||
name: Run benchmarks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Rust toolchain, cache and cargo-codspeed binary
|
||||
uses: moonrepo/setup-rust@v1
|
||||
with:
|
||||
channel: stable
|
||||
cache-target: release
|
||||
bins: cargo-codspeed
|
||||
|
||||
- name: Build the benchmark target(s)
|
||||
run: cargo codspeed build
|
||||
|
||||
- name: Run the benchmarks
|
||||
uses: CodSpeedHQ/action@v4
|
||||
with:
|
||||
mode: simulation
|
||||
run: cargo codspeed run
|
||||
@@ -1,76 +0,0 @@
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [External-testsuites]
|
||||
types: [completed]
|
||||
|
||||
name: Comment Test results on the PR
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
upload-pr-comment:
|
||||
if: ${{ github.event.workflow_run.event == 'pull_request' }}
|
||||
|
||||
name: Upload PR comment
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: List Annotations
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.event.workflow_run.id }},
|
||||
});
|
||||
|
||||
// List all artifacts
|
||||
let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "comment"
|
||||
})[0];
|
||||
|
||||
// Download the artifact to github.workspace
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
let fs = require('fs');
|
||||
fs.writeFileSync('${{ github.workspace }}/comment.zip', Buffer.from(download.data));
|
||||
|
||||
- run: unzip comment.zip
|
||||
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
let fs = require('fs');
|
||||
let annotations = JSON.parse(fs.readFileSync('./annotations.json', 'utf8'));
|
||||
|
||||
let annotationContent = annotations
|
||||
.data
|
||||
.map(annotation => `${annotation.run}: ${annotation.annotation.message}`)
|
||||
.join('\n');
|
||||
|
||||
// check if no changes
|
||||
let gnuTestReport = annotationContent.includes('Run GNU findutils tests: Gnu tests No changes');
|
||||
let bfsTestReport = annotationContent.includes('Run BFS tests: BFS tests No changes');
|
||||
|
||||
if (gnuTestReport && bfsTestReport) {
|
||||
console.log('No changes');
|
||||
return;
|
||||
}
|
||||
|
||||
// Comment on the PR
|
||||
github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: annotations.pull_request_number,
|
||||
body: 'Commit ${{ github.event.workflow_run.head_sha }} has GNU testsuite comparison:\n```\n' + annotationContent + '\n```\n'
|
||||
});
|
||||
+133
-249
@@ -1,261 +1,145 @@
|
||||
on: [push, pull_request]
|
||||
|
||||
name: External-testsuites
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
name: External testsuites
|
||||
|
||||
jobs:
|
||||
gnu-tests:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write # Publish binaries instead of discarding
|
||||
|
||||
name: Run GNU findutils tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout findutils
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: findutils
|
||||
- name: Checkout GNU findutils
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: gnu-mirror-unofficial/findutils
|
||||
path: findutils.gnu
|
||||
ref: 5768a03ddfb5e18b1682e339d6cdd24ff721c510
|
||||
submodules: false
|
||||
|
||||
- name: Override submodule URL and initialize submodules
|
||||
# Use github instead of upstream git server
|
||||
run: |
|
||||
git submodule sync --recursive
|
||||
git config submodule.gnulib.url https://github.com/coreutils/gnulib.git
|
||||
git submodule update --init --recursive --depth 1
|
||||
working-directory: findutils.gnu
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# Enable sources & install dependencies
|
||||
sudo sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources
|
||||
sudo apt-get update
|
||||
sudo apt-get build-dep findutils
|
||||
- name: Run GNU tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd findutils
|
||||
bash util/build-gnu.sh ||:
|
||||
install --strip -Dm755 target/release/{find,xargs} -t findutils-x86_64-unknown-linux-gnu
|
||||
ZSTD_CLEVEL=19 tar --zstd -caf ../findutils-x86_64-unknown-linux-gnu.tar.zst findutils-x86_64-unknown-linux-gnu
|
||||
- name: Publish latest commit
|
||||
uses: softprops/action-gh-release@v3
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
with:
|
||||
tag_name: latest-commit
|
||||
body: |
|
||||
commit: ${{ github.sha }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
files: |
|
||||
findutils-x86_64-unknown-linux-gnu.tar.zst
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract testing info
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
- name: Upload gnu-test-report
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gnu-test-report
|
||||
path: |
|
||||
findutils.gnu/find/testsuite/*.log
|
||||
findutils.gnu/xargs/testsuite/*.log
|
||||
findutils.gnu/tests/**/*.log
|
||||
- name: Upload gnu-result
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: gnu-result
|
||||
path: gnu-result.json
|
||||
- name: Download artifacts (gnu-result and gnu-test-report)
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
let fs = require('fs');
|
||||
fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });
|
||||
|
||||
async function downloadArtifact(artifactName) {
|
||||
// List all artifacts from the workflow run
|
||||
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.run_id }},
|
||||
});
|
||||
|
||||
// Find the specified artifact
|
||||
let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
|
||||
if (!matchArtifact) {
|
||||
throw new Error(`Artifact "${artifactName}" not found.`);
|
||||
}
|
||||
|
||||
// Download the artifact
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
// Save the artifact to a file
|
||||
fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
|
||||
}
|
||||
|
||||
// Download the required artifacts
|
||||
await downloadArtifact("gnu-result");
|
||||
await downloadArtifact("gnu-test-report");
|
||||
|
||||
- name: Compare failing tests against master
|
||||
shell: bash
|
||||
run: |
|
||||
./findutils/util/diff-gnu.sh ./dl ./findutils.gnu
|
||||
- name: Compare against main results
|
||||
shell: bash
|
||||
run: |
|
||||
unzip dl/gnu-result.zip -d dl/
|
||||
unzip dl/gnu-test-report.zip -d dl/
|
||||
mv dl/gnu-result.json latest-gnu-result.json
|
||||
python findutils/util/compare_gnu_result.py
|
||||
- name: Checkout findutils
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: findutils
|
||||
- name: Checkout GNU findutils
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: gnu-mirror-unofficial/findutils
|
||||
path: findutils.gnu
|
||||
ref: 5768a03ddfb5e18b1682e339d6cdd24ff721c510
|
||||
submodules: true
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# Enable sources & install dependencies
|
||||
sudo find /etc/apt/sources.list* -type f -exec sed -i 'p; s/^deb /deb-src /' '{}' +
|
||||
sudo apt-get update
|
||||
sudo apt-get build-dep findutils
|
||||
- name: Run GNU tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd findutils
|
||||
bash util/build-gnu.sh ||:
|
||||
- name: Extract testing info
|
||||
shell: bash
|
||||
run: |
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: gnu-test-report
|
||||
path: |
|
||||
findutils.gnu/find/testsuite/*.log
|
||||
findutils.gnu/xargs/testsuite/*.log
|
||||
findutils.gnu/tests/**/*.log
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: gnu-result
|
||||
path: gnu-result.json
|
||||
- name: Download the result
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: compat.yml
|
||||
workflow_conclusion: completed
|
||||
name: gnu-result
|
||||
repo: uutils/findutils
|
||||
branch: main
|
||||
path: dl
|
||||
- name: Download the log
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: compat.yml
|
||||
workflow_conclusion: completed
|
||||
name: gnu-test-report
|
||||
repo: uutils/findutils
|
||||
branch: main
|
||||
path: dl
|
||||
- name: Compare failing tests against master
|
||||
shell: bash
|
||||
run: |
|
||||
./findutils/util/diff-gnu.sh ./dl ./findutils.gnu
|
||||
- name: Compare against main results
|
||||
shell: bash
|
||||
run: |
|
||||
mv dl/gnu-result.json latest-gnu-result.json
|
||||
python findutils/util/compare_gnu_result.py
|
||||
|
||||
bfs-tests:
|
||||
name: Run BFS tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout findutils
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: findutils
|
||||
- name: Checkout BFS
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: tavianator/bfs
|
||||
path: bfs
|
||||
ref: "4.0"
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# Enable sources & install dependencies
|
||||
sudo sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources
|
||||
sudo apt-get update
|
||||
sudo apt-get build-dep bfs
|
||||
- name: Run BFS tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd findutils
|
||||
export CARGO_INCREMENTAL=0
|
||||
bash util/build-bfs.sh ||:
|
||||
- name: Upload bfs-test-report
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bfs-test-report
|
||||
path: bfs/tests.log
|
||||
- name: Upload bfs-result
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bfs-result
|
||||
path: bfs-result.json
|
||||
- name: Download artifacts (gnu-result and bfs-test-report)
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
let fs = require('fs');
|
||||
fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });
|
||||
|
||||
async function downloadArtifact(artifactName) {
|
||||
// List all artifacts from the workflow run
|
||||
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.run_id }},
|
||||
});
|
||||
|
||||
// Find the specified artifact
|
||||
let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
|
||||
if (!matchArtifact) {
|
||||
throw new Error(`Artifact "${artifactName}" not found.`);
|
||||
}
|
||||
|
||||
// Download the artifact
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
// Save the artifact to a file
|
||||
fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
|
||||
}
|
||||
|
||||
// Download the required artifacts
|
||||
await downloadArtifact("bfs-result");
|
||||
await downloadArtifact("bfs-test-report");
|
||||
- name: Compare failing tests against main
|
||||
shell: bash
|
||||
run: |
|
||||
./findutils/util/diff-bfs.sh dl/tests.log bfs/tests.log
|
||||
- name: Compare against main results
|
||||
shell: bash
|
||||
run: |
|
||||
unzip dl/bfs-result.zip -d dl/
|
||||
unzip dl/bfs-test-report.zip -d dl/
|
||||
mv dl/bfs-result.json latest-bfs-result.json
|
||||
python findutils/util/compare_bfs_result.py
|
||||
|
||||
upload-annotations:
|
||||
name: Upload annotations
|
||||
runs-on: ubuntu-latest
|
||||
needs: [gnu-tests, bfs-tests]
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
steps:
|
||||
- name: List Annotations
|
||||
uses: actions/github-script@v9
|
||||
|
||||
with:
|
||||
script: |
|
||||
let runs = await github.rest.checks.listForRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
});
|
||||
|
||||
let names = ['Run GNU findutils tests', 'Run BFS tests'];
|
||||
let results = [];
|
||||
runs.data.check_runs.filter(check => names.includes(check.name)).forEach(run => results.push(run));
|
||||
|
||||
let annotations = { data: [], pull_request_number: '${{ github.event.number }}' };
|
||||
for (let result of results) {
|
||||
let run = await github.rest.checks.listAnnotations({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
check_run_id: result.id
|
||||
});
|
||||
|
||||
run.data.forEach(data => {
|
||||
annotations.data.push({
|
||||
run: result.name,
|
||||
annotation: data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Remove duplicate items.
|
||||
annotations.data = annotations.data.filter((value, index, self) =>
|
||||
self.findIndex(v => v.annotation.message === value.annotation.message) === index);
|
||||
|
||||
let fs = require('fs');
|
||||
fs.writeFileSync('${{ github.workspace }}/annotations.json', JSON.stringify(annotations));
|
||||
|
||||
- name: Upload annotations
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: comment
|
||||
path: annotations.json
|
||||
- name: Checkout findutils
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: findutils
|
||||
- name: Checkout BFS
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: tavianator/bfs
|
||||
path: bfs
|
||||
ref: '2.6.2'
|
||||
- name: Install `rust` toolchain
|
||||
run: |
|
||||
## Install `rust` toolchain
|
||||
rustup toolchain install stable --no-self-update -c rustfmt --profile minimal
|
||||
rustup default stable
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
# Enable sources & install dependencies
|
||||
sudo find /etc/apt/sources.list* -type f -exec sed -i 'p; s/^deb /deb-src /' '{}' +
|
||||
sudo apt-get update
|
||||
sudo apt-get build-dep bfs
|
||||
- name: Run BFS tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd findutils
|
||||
bash util/build-bfs.sh ||:
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bfs-test-report
|
||||
path: bfs/tests.log
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bfs-result
|
||||
path: bfs-result.json
|
||||
- name: Download the result
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: compat.yml
|
||||
workflow_conclusion: completed
|
||||
name: bfs-result
|
||||
repo: uutils/findutils
|
||||
branch: main
|
||||
path: dl
|
||||
- name: Download the log
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: compat.yml
|
||||
workflow_conclusion: completed
|
||||
name: bfs-test-report
|
||||
repo: uutils/findutils
|
||||
branch: main
|
||||
path: dl
|
||||
- name: Compare failing tests against main
|
||||
shell: bash
|
||||
run: |
|
||||
./findutils/util/diff-bfs.sh dl/tests.log bfs/tests.log
|
||||
- name: Compare against main results
|
||||
shell: bash
|
||||
run: |
|
||||
mv dl/bfs-result.json latest-bfs-result.json
|
||||
python findutils/util/compare_bfs_result.py
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
# This file was autogenerated by dist: https://opensource.axo.dev/cargo-dist/
|
||||
#
|
||||
# Copyright 2022-2024, axodotdev
|
||||
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||
#
|
||||
# CI that:
|
||||
#
|
||||
# * checks for a Git Tag that looks like a release
|
||||
# * builds artifacts with dist (archives, installers, hashes)
|
||||
# * uploads those artifacts to temporary workflow zip
|
||||
# * on success, uploads the artifacts to a GitHub Release
|
||||
#
|
||||
# Note that the GitHub Release will be created with a generated
|
||||
# title/body based on your changelogs.
|
||||
|
||||
name: Release
|
||||
permissions:
|
||||
"contents": "write"
|
||||
|
||||
# This task will run whenever you push a git tag that looks like a version
|
||||
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
|
||||
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
|
||||
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
|
||||
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
|
||||
#
|
||||
# If PACKAGE_NAME is specified, then the announcement will be for that
|
||||
# package (erroring out if it doesn't have the given version or isn't dist-able).
|
||||
#
|
||||
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||
# (dist-able) packages in the workspace with that version (this mode is
|
||||
# intended for workspaces with only one dist-able package, or with all dist-able
|
||||
# packages versioned/released in lockstep).
|
||||
#
|
||||
# If you push multiple tags at once, separate instances of this workflow will
|
||||
# spin up, creating an independent announcement for each one. However, GitHub
|
||||
# will hard limit this to 3 tags per commit, as it will assume more tags is a
|
||||
# mistake.
|
||||
#
|
||||
# If there's a prerelease-style suffix to the version, then the release(s)
|
||||
# will be marked as a prerelease.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- '**[0-9]+.[0-9]+.[0-9]+*'
|
||||
|
||||
jobs:
|
||||
# Run 'dist plan' (or host) to determine what tasks we need to do
|
||||
plan:
|
||||
runs-on: "ubuntu-latest"
|
||||
outputs:
|
||||
val: ${{ steps.plan.outputs.manifest }}
|
||||
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
|
||||
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
|
||||
publishing: ${{ !github.event.pull_request }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install dist
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/dist
|
||||
# sure would be cool if github gave us proper conditionals...
|
||||
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
|
||||
# but also really annoying to build CI around when it needs secrets to work right.)
|
||||
- id: plan
|
||||
run: |
|
||||
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: artifacts-plan-dist-manifest
|
||||
path: plan-dist-manifest.json
|
||||
|
||||
# Build and packages all the platform-specific things
|
||||
build-local-artifacts:
|
||||
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
|
||||
# Let the initial task tell us to not run (currently very blunt)
|
||||
needs:
|
||||
- plan
|
||||
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# Target platforms/runners are computed by dist in create-release.
|
||||
# Each member of the matrix has the following arguments:
|
||||
#
|
||||
# - runner: the github runner
|
||||
# - dist-args: cli flags to pass to dist
|
||||
# - install-dist: expression to run to install dist on the runner
|
||||
#
|
||||
# Typically there will be:
|
||||
# - 1 "global" task that builds universal installers
|
||||
# - N "local" tasks that build each platform's binaries and platform-specific installers
|
||||
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
container: ${{ matrix.container && matrix.container.image || null }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
|
||||
steps:
|
||||
- name: enable windows longpaths
|
||||
run: |
|
||||
git config --global core.longpaths true
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Rust non-interactively if not already installed
|
||||
if: ${{ matrix.container }}
|
||||
run: |
|
||||
if ! command -v cargo > /dev/null 2>&1; then
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
fi
|
||||
- name: Install dist
|
||||
run: ${{ matrix.install_dist.run }}
|
||||
# Get the dist-manifest
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
${{ matrix.packages_install }}
|
||||
- name: Build artifacts
|
||||
run: |
|
||||
# Actually do builds and make zips and whatnot
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
- id: cargo-dist
|
||||
name: Post-build
|
||||
# We force bash here just because github makes it really hard to get values up
|
||||
# to "real" actions without writing to env-vars, and writing to env-vars has
|
||||
# inconsistent syntax between shell and powershell.
|
||||
shell: bash
|
||||
run: |
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
|
||||
path: |
|
||||
${{ steps.cargo-dist.outputs.paths }}
|
||||
${{ env.BUILD_MANIFEST_NAME }}
|
||||
|
||||
# Build and package all the platform-agnostic(ish) things
|
||||
build-global-artifacts:
|
||||
needs:
|
||||
- plan
|
||||
- build-local-artifacts
|
||||
runs-on: "ubuntu-latest"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
- id: cargo-dist
|
||||
shell: bash
|
||||
run: |
|
||||
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||
echo "dist ran successfully"
|
||||
|
||||
# Parse out what we just built and upload it to scratch storage
|
||||
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: artifacts-build-global
|
||||
path: |
|
||||
${{ steps.cargo-dist.outputs.paths }}
|
||||
${{ env.BUILD_MANIFEST_NAME }}
|
||||
# Determines if we should publish/announce
|
||||
host:
|
||||
needs:
|
||||
- plan
|
||||
- build-local-artifacts
|
||||
- build-global-artifacts
|
||||
# Only run if we're "publishing", and only if local and global didn't fail (skipped is fine)
|
||||
if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: "ubuntu-latest"
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
merge-multiple: true
|
||||
- id: host
|
||||
shell: bash
|
||||
run: |
|
||||
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||
echo "artifacts uploaded and released successfully"
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
# Overwrite the previous copy
|
||||
name: artifacts-dist-manifest
|
||||
path: dist-manifest.json
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
- name: "Download GitHub Artifacts"
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
- name: Cleanup
|
||||
run: |
|
||||
# Remove the granular manifests
|
||||
rm -f artifacts/*-dist-manifest.json
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
|
||||
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
|
||||
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
|
||||
RELEASE_COMMIT: "${{ github.sha }}"
|
||||
run: |
|
||||
# Write and read notes from a file to avoid quoting breaking things
|
||||
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
|
||||
|
||||
gh release create "${{ needs.plan.outputs.tag }}" --draft --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
|
||||
|
||||
announce:
|
||||
needs:
|
||||
- plan
|
||||
- host
|
||||
# use "always() && ..." to allow us to wait for all publish jobs while
|
||||
# still allowing individual publish jobs to skip themselves (for prereleases).
|
||||
# "host" however must run to completion, no skipping allowed!
|
||||
if: ${{ always() && needs.host.result == 'success' }}
|
||||
runs-on: "ubuntu-latest"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
@@ -4,4 +4,3 @@ target
|
||||
.cargo
|
||||
.settings
|
||||
test_data/links/link-*
|
||||
/public/
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: rust-linting
|
||||
name: Rust linting
|
||||
description: Run cargo fmt on files included in the commit.
|
||||
entry: cargo +nightly fmt --
|
||||
pass_filenames: true
|
||||
types: [file, rust]
|
||||
language: system
|
||||
- id: rust-clippy
|
||||
name: Rust clippy
|
||||
description: Run cargo clippy on files included in the commit.
|
||||
entry: cargo +nightly clippy --workspace --all-targets --all-features --
|
||||
pass_filenames: false
|
||||
types: [file, rust]
|
||||
language: system
|
||||
ci:
|
||||
skip: [rust-linting, rust-clippy]
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
language: rust
|
||||
|
||||
sudo: false
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
rust:
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
cache: cargo
|
||||
|
||||
before_cache: |
|
||||
if [[ "$TRAVIS_RUST_VERSION" = nightly ]]; then
|
||||
RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-tarpaulin -f
|
||||
fi
|
||||
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- cargo test --verbose
|
||||
|
||||
after_script: |
|
||||
if [[ "$TRAVIS_OS_NAME" = linux && "$TRAVIS_RUST_VERSION" = nightly ]]; then
|
||||
cargo tarpaulin --out Xml
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
fi
|
||||
@@ -1,128 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
sylvestre@debian.org.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
<https://www.contributor-covenant.org/faq>. Translations are available at
|
||||
<https://www.contributor-covenant.org/translations>.
|
||||
-262
@@ -1,262 +0,0 @@
|
||||
<!-- spell-checker:ignore reimplementing toybox RUNTEST CARGOFLAGS nextest embeddable Rustonomicon rustdoc's -->
|
||||
|
||||
# Contributing to findutils
|
||||
|
||||
Hi! Welcome to uutils/findutils!
|
||||
|
||||
Thanks for wanting to contribute to this project! This document explains
|
||||
everything you need to know to contribute. Before you start make sure to also
|
||||
check out these documents:
|
||||
|
||||
- Our community's [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
||||
- [DEVELOPMENT.md](./DEVELOPMENT.md) for setting up your development
|
||||
environment.
|
||||
- Our [Review Guidelines](https://uutils.github.io/reviews/) for what we expect
|
||||
from a pull request and how reviews are done.
|
||||
|
||||
Now follows a very important warning:
|
||||
|
||||
> [!WARNING]
|
||||
> uutils is original code and cannot contain any code from GNU or
|
||||
> other implementations. This means that **we cannot accept any changes based on
|
||||
> the GNU source code**. To make sure that cannot happen, **you cannot link to
|
||||
> the GNU source code** either. It is however possible to look at other implementations
|
||||
> under a BSD or MIT license like [Apple's implementation](https://opensource.apple.com/source/file_cmds/)
|
||||
> or [OpenBSD](https://github.com/openbsd/src/tree/master/bin).
|
||||
|
||||
Finally, feel free to join our [Discord](https://discord.gg/wQVJbvJ)!
|
||||
|
||||
<!-- TODO: Getting Oriented -->
|
||||
|
||||
## Design Goals
|
||||
|
||||
We have the following goals with our development:
|
||||
|
||||
- **Compatible**: The utilities should be a drop-in replacement for the GNU
|
||||
coreutils.
|
||||
- **Cross-platform**: All utilities should run on as many of the supported
|
||||
platforms as possible.
|
||||
- **Reliable**: The utilities should never unexpectedly fail.
|
||||
- **Performant**: Our utilities should be written in fast idiomatic Rust. We aim
|
||||
to match or exceed the performance of the GNU utilities.
|
||||
- **Well-tested**: We should have a lot of tests to be able to guarantee
|
||||
reliability and compatibility.
|
||||
|
||||
## How to Help
|
||||
|
||||
There are several ways to help and writing code is just one of them. Reporting
|
||||
issues and writing documentation are just as important as writing code.
|
||||
|
||||
### Reporting Issues
|
||||
|
||||
We can't fix bugs we don't know about, so good issues are super helpful! Here
|
||||
are some tips for writing good issues:
|
||||
|
||||
- If you find a bug, make sure it's still a problem on the `main` branch.
|
||||
- Search through the existing issues to see whether it has already been
|
||||
reported.
|
||||
- Make sure to include all relevant information, such as:
|
||||
- Which version of uutils did you check?
|
||||
- Which version of GNU coreutils are you comparing with?
|
||||
- What platform are you on?
|
||||
- Provide a way to reliably reproduce the issue.
|
||||
- Be as specific as possible!
|
||||
|
||||
### Writing Documentation
|
||||
|
||||
There's never enough documentation. If you come across any documentation that
|
||||
could be improved, feel free to submit a PR for it!
|
||||
|
||||
### Writing Code
|
||||
|
||||
If you want to submit a PR, make sure that you've discussed the solution with
|
||||
the maintainers beforehand. We want to avoid situations where you put a lot of
|
||||
work into a fix that we can't merge! If there's no issue for what you're trying
|
||||
to fix yet, make one _before_ you start working on the PR.
|
||||
|
||||
Generally, we try to follow what GNU is doing in terms of options and behavior.
|
||||
It is recommended to look at the GNU findtils manual
|
||||
([on the web](https://www.gnu.org/software/findutils/manual/html_node/index.html),
|
||||
or locally using `info <utility>`). It is more in depth than the man pages and
|
||||
provides a good description of available features and their implementation
|
||||
details. But remember, you cannot look at the GNU source code!
|
||||
|
||||
Also remember that we can only merge PRs which pass our test suite, follow
|
||||
rustfmt, and do not have any warnings from clippy. See
|
||||
[DEVELOPMENT.md](./DEVELOPMENT.md) for more information. Be sure to also read
|
||||
about our [Rust style](#our-rust-style).
|
||||
|
||||
## Our Rust Style
|
||||
|
||||
We want uutils to be written in idiomatic Rust, so here are some guidelines to
|
||||
follow. Some of these are aspirational, meaning that we don't do them correctly
|
||||
everywhere in the code. If you find violations of the advice below, feel free to
|
||||
submit a patch!
|
||||
|
||||
### Don't `panic!`
|
||||
|
||||
The coreutils should be very reliable. This means that we should never `panic!`.
|
||||
Therefore, you should avoid using `.unwrap()` and `panic!`. Sometimes the use of
|
||||
`unreachable!` can be justified with a comment explaining why that code is
|
||||
unreachable.
|
||||
|
||||
### Don't `exit`
|
||||
|
||||
We want uutils to be embeddable in other programs. This means that no function
|
||||
in uutils should exit the program. Doing so would also lead to code with more
|
||||
confusing control flow. Avoid therefore `std::process::exit` and similar
|
||||
functions which exit the program early.
|
||||
|
||||
### `unsafe`
|
||||
|
||||
uutils cannot be entirely safe, because we have to call out to `libc` and do
|
||||
syscalls. However, we still want to limit our use of `unsafe`. We generally only
|
||||
accept `unsafe` for FFI, with very few exceptions. Note that performance is very
|
||||
rarely a valid argument for using `unsafe`.
|
||||
|
||||
If you still need to write code with `unsafe`, make sure to read the
|
||||
[Rustonomicon](https://doc.rust-lang.org/nomicon/intro.html) and annotate the
|
||||
calls with `// SAFETY:` comments explaining why the use of `unsafe` is sound.
|
||||
|
||||
### Macros
|
||||
|
||||
Macros can be a great tool, but they are also usually hard to understand. They
|
||||
should be used sparingly. Make sure to explore simpler options before you reach
|
||||
for a solution involving macros.
|
||||
|
||||
### `str`, `OsStr` & `Path`
|
||||
|
||||
Rust has many string-like types, and sometimes it's hard to choose the right
|
||||
one. It's tempting to use `str` (and `String`) for everything, but that is not
|
||||
always the right choice for uutils, because we need to support invalid UTF-8,
|
||||
just like the GNU coreutils. For example, paths on Linux might not be valid
|
||||
UTF-8! Whenever we are dealing with paths, we should therefore stick with
|
||||
`OsStr` and `Path`. Make sure that you only convert to `str`/`String` if you
|
||||
know that something is always valid UTF-8. If you need more operations on
|
||||
`OsStr`, you can use the [`bstr`](https://docs.rs/bstr/latest/bstr/) crate.
|
||||
|
||||
### Doc-comments
|
||||
|
||||
We use rustdoc for our documentation, so it's best to follow
|
||||
[rustdoc's guidelines](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#documenting-components).
|
||||
Make sure that your documentation is not just repeating the name of the
|
||||
function, but actually giving more useful information. Rustdoc recommends the
|
||||
following structure:
|
||||
|
||||
```
|
||||
[short sentence explaining what it is]
|
||||
|
||||
[more detailed explanation]
|
||||
|
||||
[at least one code example that users can copy/paste to try it]
|
||||
|
||||
[even more advanced explanations if necessary]
|
||||
```
|
||||
|
||||
### Other comments
|
||||
|
||||
Comments should be written to _explain_ the code, not to _describe_ the code.
|
||||
Try to focus on explaining _why_ the code is the way it is. If you feel like you
|
||||
have to describe the code, that's usually a sign that you could improve the
|
||||
naming of variables and functions.
|
||||
|
||||
If you edit a piece of code, make sure to update any comments that need to
|
||||
change as a result. The only thing worse than having no comments is having
|
||||
outdated comments!
|
||||
|
||||
## Git Etiquette
|
||||
|
||||
To ensure easy collaboration, we have guidelines for using Git and GitHub.
|
||||
|
||||
### Commits
|
||||
|
||||
- Make small and atomic commits.
|
||||
- Keep a clean history of commits.
|
||||
- Write informative commit messages.
|
||||
- Annotate your commit message with the component you're editing. For example:
|
||||
`cp: do not overwrite on with -i` or `uucore: add support for FreeBSD`.
|
||||
- Do not unnecessarily move items around in the code. This makes the changes
|
||||
much harder to review. If you do need to move things around, do that in a
|
||||
separate commit.
|
||||
|
||||
### Commit messages
|
||||
|
||||
You can read this section in the Git book to learn how to write good commit
|
||||
messages: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project.
|
||||
|
||||
In addition, here are a few examples for a summary line when committing to
|
||||
uutils:
|
||||
|
||||
- commit for a single utility
|
||||
|
||||
```
|
||||
nohup: cleanup and refactor
|
||||
```
|
||||
|
||||
- commit for a utility's tests
|
||||
|
||||
```
|
||||
tests/rm: test new feature
|
||||
```
|
||||
|
||||
Beyond changes to an individual utility or its tests, other summary lines for
|
||||
non-utility modules include:
|
||||
|
||||
```
|
||||
README: add help
|
||||
uucore: add new modules
|
||||
uutils: add new utility
|
||||
gitignore: add temporary files
|
||||
```
|
||||
|
||||
### PRs
|
||||
|
||||
- Make the titles of PRs descriptive.
|
||||
- This means describing the problem you solve. For example, do not write
|
||||
`Fix #1234`, but `ls: fix version sort order`.
|
||||
- You can prefix the title with the utility the PR concerns.
|
||||
- Keep PRs small and self-contained. A set of small PRs is much more likely to
|
||||
get merged quickly than one large PR.
|
||||
- Make sure the CI passes (up to intermittently failing tests).
|
||||
- You know your code best, that's why it's best if you can solve merge conflicts
|
||||
on your branch yourself.
|
||||
- It's up to you whether you want to use `git merge main` or
|
||||
`git rebase main`.
|
||||
- Feel free to ask for help with merge conflicts.
|
||||
- You do not need to ping maintainers to request a review, but it's fine to do
|
||||
so if you don't get a response within a few days.
|
||||
|
||||
## Platforms
|
||||
|
||||
We take pride in supporting many operating systems and architectures. Any code
|
||||
you contribute must at least compile without warnings for all platforms in the
|
||||
CI. However, you can use `#[cfg(...)]` attributes to create platform dependent
|
||||
features.
|
||||
|
||||
## Licensing
|
||||
|
||||
uutils is distributed under the terms of the MIT License; see the `LICENSE` file
|
||||
for details. This is a permissive license, which allows the software to be used
|
||||
with few restrictions.
|
||||
|
||||
Copyrights in the uutils project are retained by their contributors, and no
|
||||
copyright assignment is required to contribute.
|
||||
|
||||
If you wish to add or change dependencies as part of a contribution to the
|
||||
project, a tool like `cargo-license` can be used to show their license details.
|
||||
The following types of license are acceptable:
|
||||
|
||||
- MIT License
|
||||
- Dual- or tri-license with an MIT License option ("Apache-2.0 or MIT" is a
|
||||
popular combination)
|
||||
- "MIT equivalent" license (2-clause BSD, 3-clause BSD, ISC)
|
||||
- License less restrictive than the MIT License (CC0 1.0 Universal)
|
||||
- Apache License version 2.0
|
||||
|
||||
Licenses we will not use:
|
||||
|
||||
- An ambiguous license, or no license
|
||||
- Strongly reciprocal licenses (GNU GPL, GNU LGPL)
|
||||
|
||||
If you wish to add a reference but it doesn't meet these requirements, please
|
||||
raise an issue to describe the dependency.
|
||||
Generated
+571
-1559
File diff suppressed because it is too large
Load Diff
+12
-91
@@ -1,65 +1,36 @@
|
||||
[package]
|
||||
name = "findutils"
|
||||
version = "0.9.0"
|
||||
version = "0.3.0"
|
||||
homepage = "https://github.com/uutils/findutils"
|
||||
repository = "https://github.com/uutils/findutils"
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
description = "Rust implementation of GNU findutils"
|
||||
authors = ["uutils developers"]
|
||||
|
||||
[dependencies]
|
||||
argmax = "0.4.0"
|
||||
chrono = "0.4.45"
|
||||
clap = { version = "4.6", features = ["env"] }
|
||||
chrono = "0.4"
|
||||
clap = "2.34"
|
||||
faccess = "0.2.4"
|
||||
nix = { version = "0.31", features = ["fs", "user"] }
|
||||
onig = { version = "6.5", default-features = false }
|
||||
regex = "1.12"
|
||||
uucore = { version = "0.9.0", features = ["entries", "fs", "fsext", "mode"] }
|
||||
walkdir = "2.5"
|
||||
itertools = "0.14.0"
|
||||
thiserror = "2.0.12"
|
||||
walkdir = "2.3"
|
||||
regex = "1.7"
|
||||
once_cell = "1.17"
|
||||
onig = "6.4"
|
||||
uucore = { version = "0.0.12", features = ["entries", "fs", "fsext", "mode"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "4.7.0", package = "codspeed-criterion-compat" }
|
||||
assert_cmd = "2"
|
||||
ctor = "1.0"
|
||||
filetime = "0.2"
|
||||
nix = { version = "0.31", features = ["fs"] }
|
||||
rstest = "0.26.1"
|
||||
nix = "0.26"
|
||||
predicates = "2"
|
||||
serial_test = "1.0"
|
||||
tempfile = "3"
|
||||
uutests = "0.9.0"
|
||||
|
||||
[[bench]]
|
||||
name = "find_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "xargs_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "updatedb_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "locate_bench"
|
||||
harness = false
|
||||
|
||||
[[bin]]
|
||||
name = "find"
|
||||
path = "src/find/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "locate"
|
||||
path = "src/locate/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "updatedb"
|
||||
path = "src/updatedb/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "xargs"
|
||||
path = "src/xargs/main.rs"
|
||||
@@ -67,53 +38,3 @@ path = "src/xargs/main.rs"
|
||||
[[bin]]
|
||||
name = "testing-commandline"
|
||||
path = "src/testing/commandline/main.rs"
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.release-fast]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
|
||||
[lints.clippy]
|
||||
all = { level = "warn", priority = -1 }
|
||||
cargo = { level = "warn", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
use_self = "warn" # nursery lint
|
||||
|
||||
# Group-level allows for lints that are too noisy or not worth enforcing for now.
|
||||
multiple_crate_versions = "allow"
|
||||
cargo_common_metadata = "allow"
|
||||
uninlined_format_args = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
must_use_candidate = "allow"
|
||||
match_same_arms = "allow"
|
||||
cast_possible_truncation = "allow"
|
||||
too_many_lines = "allow"
|
||||
cast_possible_wrap = "allow"
|
||||
cast_sign_loss = "allow"
|
||||
struct_excessive_bools = "allow"
|
||||
cast_precision_loss = "allow"
|
||||
cast_lossless = "allow"
|
||||
ignored_unit_patterns = "allow"
|
||||
similar_names = "allow"
|
||||
float_cmp = "allow"
|
||||
return_self_not_must_use = "allow"
|
||||
inline_always = "allow"
|
||||
fn_params_excessive_bools = "allow"
|
||||
used_underscore_items = "allow"
|
||||
should_panic_without_expect = "allow"
|
||||
doc_markdown = "allow"
|
||||
unused_self = "allow"
|
||||
enum_glob_use = "allow"
|
||||
unnested_or_patterns = "allow"
|
||||
implicit_hasher = "allow"
|
||||
doc_link_with_quotes = "allow"
|
||||
format_push_string = "allow"
|
||||
flat_map_option = "allow"
|
||||
from_iter_instead_of_collect = "allow"
|
||||
large_types_passed_by_value = "allow"
|
||||
# Disable for now, we have a few occurrences
|
||||
# panic = "warn"
|
||||
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
<!-- spell-checker:ignore (flags) Ccodegen Coverflow Cpanic Zinstrument Zpanic reimplementing toybox RUNTEST CARGOFLAGS nextest prereq autopoint gettext texinfo automake findutils shellenv libexec gnubin toolchains gsed -->
|
||||
|
||||
# Setting up your local development environment
|
||||
|
||||
For contributing rules and best practices please refer to [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## Before you start
|
||||
|
||||
For this guide we assume that you already have a GitHub account and have `git` and your favorite code editor or IDE installed and configured.
|
||||
Before you start working on findutils, please follow these steps:
|
||||
|
||||
1. Fork the [findutils repository](https://github.com/uutils/findutils) to your GitHub account.
|
||||
***Tip:*** See [this GitHub guide](https://docs.github.com/en/get-started/quickstart/fork-a-repo) for more information on this step.
|
||||
2. Clone that fork to your local development environment:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/YOUR-GITHUB-ACCOUNT/findutils
|
||||
cd findutils
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
You will need the tools mentioned in this section to build and test your code changes locally.
|
||||
This section will explain how to install and configure these tools.
|
||||
We also have an extensive CI that uses these tools and will check your code before it can be merged.
|
||||
The next section [Testing](#testing) will explain how to run those checks locally to avoid waiting for the CI.
|
||||
|
||||
### Rust toolchain
|
||||
|
||||
[Install Rust](https://www.rust-lang.org/tools/install)
|
||||
|
||||
If you're using rustup to install and manage your Rust toolchains, `clippy` and `rustfmt` are usually already installed. If you are using one of the alternative methods, please make sure to install them manually. See following sub-sections for their usage: [clippy](#clippy) [rustfmt](#rustfmt).
|
||||
|
||||
***Tip*** You might also need to add 'llvm-tools' component if you are going to [generate code coverage reports locally](#code-coverage-report):
|
||||
|
||||
```shell
|
||||
rustup component add llvm-tools-preview
|
||||
```
|
||||
|
||||
### pre-commit hooks
|
||||
|
||||
A configuration for `pre-commit` is provided in the repository. It allows
|
||||
automatically checking every git commit you make to ensure it compiles, and
|
||||
passes `clippy` and `rustfmt` without warnings.
|
||||
|
||||
To use the provided hook:
|
||||
|
||||
1. [Install `pre-commit`](https://pre-commit.com/#install)
|
||||
1. Run `pre-commit install` while in the repository directory
|
||||
|
||||
Your git commits will then automatically be checked. If a check fails, an error
|
||||
message will explain why, and your commit will be canceled. You can then make
|
||||
the suggested changes, and run `git commit ...` again.
|
||||
|
||||
**NOTE: On MacOS** the pre-commit hooks are currently broken. There are workarounds involving switching to unstable nightly Rust and components.
|
||||
|
||||
### clippy
|
||||
|
||||
```shell
|
||||
cargo clippy --all-targets --all-features
|
||||
```
|
||||
|
||||
The `msrv` key in the clippy configuration file `clippy.toml` is used to disable
|
||||
lints pertaining to newer features by specifying the minimum supported Rust
|
||||
version (MSRV).
|
||||
|
||||
### rustfmt
|
||||
|
||||
```shell
|
||||
cargo fmt --all
|
||||
```
|
||||
|
||||
### cargo-deny
|
||||
|
||||
This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to
|
||||
detect duplicate dependencies, checks licenses, etc. To run it locally, first
|
||||
install it and then run with:
|
||||
|
||||
```shell
|
||||
cargo deny --all-features check all
|
||||
```
|
||||
|
||||
### Markdown linter
|
||||
|
||||
We use [markdownlint](https://github.com/DavidAnson/markdownlint) to lint the
|
||||
Markdown files in the repository.
|
||||
|
||||
### Spell checker
|
||||
|
||||
We use `cspell` as spell checker for all files in the project. If you are using
|
||||
VS Code, you can install the
|
||||
[code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
|
||||
extension to enable spell checking within your editor. Otherwise, you can
|
||||
install [cspell](https://cspell.org/) separately.
|
||||
|
||||
If you want to make the spell checker ignore a word, you can add
|
||||
|
||||
```rust
|
||||
// spell-checker:ignore word_to_ignore
|
||||
```
|
||||
|
||||
at the top of the file.
|
||||
|
||||
## Testing
|
||||
|
||||
Just like with building, we follow the standard procedure for testing using
|
||||
Cargo:
|
||||
|
||||
```shell
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Code coverage report
|
||||
|
||||
Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov).
|
||||
|
||||
### Using Nightly Rust
|
||||
|
||||
To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report
|
||||
|
||||
```shell
|
||||
export CARGO_INCREMENTAL=0
|
||||
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||
export RUSTDOCFLAGS="-Cpanic=abort"
|
||||
cargo build <options...>
|
||||
cargo test <options...>
|
||||
grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/
|
||||
# open target/debug/coverage/index.html in browser
|
||||
```
|
||||
|
||||
if changes are not reflected in the report then run `cargo clean` and run the above commands.
|
||||
|
||||
### Using Stable Rust
|
||||
|
||||
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
|
||||
then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above.
|
||||
|
||||
## Tips for setting up on Mac
|
||||
|
||||
### C Compiler and linker
|
||||
|
||||
On MacOS you'll need to install C compiler & linker:
|
||||
|
||||
```shell
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
## Tips for setting up on Windows
|
||||
|
||||
### MSVC build tools
|
||||
|
||||
On Windows you'll need the MSVC build tools for Visual Studio 2013 or later.
|
||||
|
||||
If you are using `rustup-init.exe` to install Rust toolchain, it will guide you through the process of downloading and installing these prerequisites.
|
||||
|
||||
Otherwise please follow [this guide](https://learn.microsoft.com/en-us/windows/dev-environment/rust/setup).
|
||||
@@ -1,13 +1,10 @@
|
||||
# findutils
|
||||
|
||||
[](https://crates.io/crates/findutils)
|
||||
[](https://discord.gg/wQVJbvJ)
|
||||
[](https://github.com/uutils/findutils/blob/main/LICENSE)
|
||||
[](https://deps.rs/repo/github/uutils/findutils)
|
||||
[](https://codecov.io/gh/uutils/findutils)
|
||||
|
||||
Rust implementation of [GNU findutils](https://www.gnu.org/software/findutils/): `xargs`, `find`, `locate` and `updatedb`.
|
||||
The goal is to be a full drop-in replacement of the original commands.
|
||||
Rust implementation of [GNU findutils](https://www.gnu.org/software/findutils/).
|
||||
|
||||
## Run the GNU testsuite on rust/findutils:
|
||||
|
||||
@@ -20,18 +17,7 @@ bash util/build-gnu.sh tests/misc/help-version.sh
|
||||
|
||||
## Comparing with GNU
|
||||
|
||||

|
||||

|
||||
|
||||
## Build/run with BFS
|
||||
|
||||
[bfs](https://github.com/tavianator/bfs) is a variant of the UNIX find command that operates breadth-first rather than depth-first.
|
||||
|
||||
```
|
||||
bash util/build-bfs.sh
|
||||
|
||||
# To run a specific test:
|
||||
bash util/build-bfs.sh posix/basic
|
||||
```
|
||||

|
||||

|
||||
|
||||
For more details, see https://github.com/uutils/findutils-tracking/
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We provide security updates only for the latest released version of `uutils/findutils`.
|
||||
Older versions may not receive patches.
|
||||
If you are using a version packaged by your Linux distribution, please check with your distribution maintainers for their update policy.
|
||||
|
||||
---
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**Do not open public GitHub issues for security vulnerabilities.**
|
||||
This prevents accidental disclosure before a fix is available.
|
||||
|
||||
Instead, please use the following method:
|
||||
|
||||
- **Email:** [sylvestre@debian.org](mailto:Sylvestre@debian.org)
|
||||
- **Encryption (optional):** You may encrypt your report using our PGP key:
|
||||
Fingerprint: B60D B599 4D39 BEC4 D1A9 5CCF 7E65 28DA 752F 1BE1
|
||||
---
|
||||
|
||||
### What to Include in Your Report
|
||||
|
||||
To help us investigate and resolve the issue quickly, please include as much detail as possible:
|
||||
|
||||
- **Type of issue:** e.g. privilege escalation, information disclosure.
|
||||
- **Location in the source:** file path, commit hash, branch, or tag.
|
||||
- **Steps to reproduce:** exact commands, test cases, or scripts.
|
||||
- **Special configuration:** any flags, environment variables, or system setup required.
|
||||
- **Affected systems:** OS/distribution and version(s) where the issue occurs.
|
||||
- **Impact:** your assessment of the potential severity (DoS, RCE, data leak, etc.).
|
||||
|
||||
---
|
||||
|
||||
## Disclosure Policy
|
||||
|
||||
We follow a **Coordinated Vulnerability Disclosure (CVD)** process:
|
||||
|
||||
1. We will acknowledge receipt of your report within **10 days**.
|
||||
2. We will investigate, reproduce, and assess the issue.
|
||||
3. We will provide a timeline for developing and releasing a fix.
|
||||
4. Once a fix is available, we will publish a GitHub Security Advisory.
|
||||
5. You will be credited in the advisory unless you request anonymity.
|
||||
@@ -1,166 +0,0 @@
|
||||
// Copyright 2024 the uutils developers
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
//! End-to-end benchmarks for `find`, run through the real `find_main` entry
|
||||
//! point so they exercise argument parsing, the matcher tree and the directory
|
||||
//! walk together. Output is sent to a sink so the benchmarks measure the work
|
||||
//! `find` does rather than terminal I/O.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use findutils::find::{find_main, Dependencies};
|
||||
|
||||
/// `Dependencies` implementation that throws output away. A fixed `now` keeps
|
||||
/// time-based matchers (`-newer`, `-mtime`, …) deterministic across runs.
|
||||
struct SinkDependencies {
|
||||
output: RefCell<io::Sink>,
|
||||
now: SystemTime,
|
||||
}
|
||||
|
||||
impl SinkDependencies {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
output: RefCell::new(io::sink()),
|
||||
now: SystemTime::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dependencies for SinkDependencies {
|
||||
fn get_output(&self) -> &RefCell<dyn Write> {
|
||||
&self.output
|
||||
}
|
||||
|
||||
fn now(&self) -> SystemTime {
|
||||
self.now
|
||||
}
|
||||
|
||||
fn confirm(&self, _prompt: &str) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Run `find` end-to-end. `args` are the arguments after the program name
|
||||
/// (flags, paths, expression). The exit status is ignored — we only care about
|
||||
/// the work performed.
|
||||
fn run(args: &[&str]) {
|
||||
let mut argv: Vec<&str> = Vec::with_capacity(args.len() + 1);
|
||||
argv.push("find");
|
||||
argv.extend_from_slice(args);
|
||||
let deps = SinkDependencies::new();
|
||||
let _ = find_main(&argv, &deps);
|
||||
}
|
||||
|
||||
/// Build a moderately deep directory tree to walk. `depth` levels, each holding
|
||||
/// `dirs` sub-directories and `files` files. File names vary so name/regex
|
||||
/// matchers have something to discriminate on, and sizes vary so `-size` does
|
||||
/// real work. Returns the root directory.
|
||||
fn build_tree(depth: u32, dirs: u32, files: u32) -> PathBuf {
|
||||
let root = std::env::temp_dir().join(format!("uu_find_bench_{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
populate(&root, depth, dirs, files, 0);
|
||||
root
|
||||
}
|
||||
|
||||
fn populate(dir: &Path, depth: u32, dirs: u32, files: u32, seed: u32) {
|
||||
for f in 0..files {
|
||||
let n = seed.wrapping_add(f);
|
||||
// A mix of extensions and a sprinkling of a rare marker name.
|
||||
let name = match n % 5 {
|
||||
0 => format!("module_{n}.rs"),
|
||||
1 => format!("data_{n}.txt"),
|
||||
2 => format!("image_{n}.png"),
|
||||
3 => format!("README_{n}.md"),
|
||||
_ if n % 500 == 0 => format!("RAREHIT_{n}.log"),
|
||||
_ => format!("file_{n}.log"),
|
||||
};
|
||||
// Sizes from a few bytes up to ~8 KiB so -size buckets differ.
|
||||
let size = (n as usize % 8192) + 1;
|
||||
let path = dir.join(name);
|
||||
std::fs::write(&path, vec![b'x'; size]).unwrap();
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
for d in 0..dirs {
|
||||
let sub = dir.join(format!("dir_{d}"));
|
||||
std::fs::create_dir_all(&sub).unwrap();
|
||||
populate(
|
||||
&sub,
|
||||
depth - 1,
|
||||
dirs,
|
||||
files,
|
||||
seed.wrapping_add((d + 1) * 31),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_e2e(c: &mut Criterion) {
|
||||
// depth 4, 4 dirs/level, 25 files/dir → a few thousand entries.
|
||||
let root = build_tree(4, 4, 25);
|
||||
let dir = root.to_str().unwrap();
|
||||
|
||||
let mut group = c.benchmark_group("find");
|
||||
|
||||
// Plain full walk with the implicit -print.
|
||||
group.bench_function("walk_all", |b| {
|
||||
b.iter(|| run(black_box(&[dir])));
|
||||
});
|
||||
// Filter by file type only.
|
||||
group.bench_function("type_f", |b| {
|
||||
b.iter(|| run(black_box(&[dir, "-type", "f"])));
|
||||
});
|
||||
// Glob name match — a common invocation.
|
||||
group.bench_function("name_glob", |b| {
|
||||
b.iter(|| run(black_box(&[dir, "-name", "*.rs"])));
|
||||
});
|
||||
// Case-insensitive name match.
|
||||
group.bench_function("iname_glob", |b| {
|
||||
b.iter(|| run(black_box(&[dir, "-iname", "*.RS"])));
|
||||
});
|
||||
// Regex over the whole path.
|
||||
group.bench_function("regex_path", |b| {
|
||||
b.iter(|| run(black_box(&[dir, "-regex", r".*/module_[0-9]+\.rs"])));
|
||||
});
|
||||
// Size predicate forces a stat per entry.
|
||||
group.bench_function("size_gt", |b| {
|
||||
b.iter(|| run(black_box(&[dir, "-type", "f", "-size", "+4k"])));
|
||||
});
|
||||
// Combined predicate with AND/OR and grouping.
|
||||
group.bench_function("combined_expr", |b| {
|
||||
b.iter(|| {
|
||||
run(black_box(&[
|
||||
dir, "-type", "f", "(", "-name", "*.rs", "-o", "-name", "*.md", ")",
|
||||
]));
|
||||
});
|
||||
});
|
||||
// Prune whole subtrees, then print the rest.
|
||||
group.bench_function("prune", |b| {
|
||||
b.iter(|| {
|
||||
run(black_box(&[
|
||||
dir, "-name", "dir_0", "-prune", "-o", "-type", "f", "-print",
|
||||
]));
|
||||
});
|
||||
});
|
||||
// -printf with several directives exercises the formatter.
|
||||
group.bench_function("printf", |b| {
|
||||
b.iter(|| run(black_box(&[dir, "-type", "f", "-printf", "%p %s %y\\n"])));
|
||||
});
|
||||
|
||||
group.finish();
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_e2e);
|
||||
criterion_main!(benches);
|
||||
@@ -1,127 +0,0 @@
|
||||
// Copyright 2024 the uutils developers
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
//! End-to-end benchmarks for `locate`, run through the real `locate_main` entry
|
||||
//! point. A LOCATE02 database is built once (via `updatedb`) from a generated
|
||||
//! tree, then each case scans it in `-c`/count mode so the database decoding
|
||||
//! and pattern matching dominate rather than terminal output.
|
||||
//!
|
||||
//! `locate` is Unix-only (it relies on `OsStr`/`CStr` byte APIs), so the
|
||||
//! benchmark body is compiled only there.
|
||||
|
||||
#[cfg(unix)]
|
||||
mod bench {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use criterion::{black_box, criterion_group, Criterion};
|
||||
use findutils::locate::locate_main;
|
||||
use findutils::updatedb::updatedb_main;
|
||||
|
||||
/// Run `locate` end-to-end. `args` are the arguments after the program name.
|
||||
fn run(args: &[&str]) {
|
||||
let mut argv: Vec<&str> = Vec::with_capacity(args.len() + 1);
|
||||
argv.push("locate");
|
||||
argv.extend_from_slice(args);
|
||||
let _ = locate_main(&argv);
|
||||
}
|
||||
|
||||
fn build_tree(depth: u32, dirs: u32, files: u32) -> PathBuf {
|
||||
let root = std::env::temp_dir().join(format!("uu_locate_bench_{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
populate(&root, depth, dirs, files, 0);
|
||||
root
|
||||
}
|
||||
|
||||
fn populate(dir: &Path, depth: u32, dirs: u32, files: u32, seed: u32) {
|
||||
for f in 0..files {
|
||||
let n = seed.wrapping_add(f);
|
||||
let name = match n % 4 {
|
||||
0 => format!("module_{n}.rs"),
|
||||
1 => format!("data_{n}.txt"),
|
||||
2 => format!("image_{n}.png"),
|
||||
_ => format!("file_{n}.log"),
|
||||
};
|
||||
std::fs::write(dir.join(name), b"x").unwrap();
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
for d in 0..dirs {
|
||||
let sub = dir.join(format!("dir_{d}"));
|
||||
std::fs::create_dir_all(&sub).unwrap();
|
||||
populate(
|
||||
&sub,
|
||||
depth - 1,
|
||||
dirs,
|
||||
files,
|
||||
seed.wrapping_add((d + 1) * 31),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bench_e2e(c: &mut Criterion) {
|
||||
// ~8.5k paths, then front-coded into a LOCATE02 database once.
|
||||
let root = build_tree(4, 4, 25);
|
||||
let dir = root.to_str().unwrap();
|
||||
let db = std::env::temp_dir().join(format!("uu_locate_bench_{}.db", std::process::id()));
|
||||
let db_path = db.to_str().unwrap();
|
||||
|
||||
let _ = updatedb_main(&[
|
||||
"updatedb",
|
||||
&format!("--localpaths={dir}"),
|
||||
&format!("--output={db_path}"),
|
||||
"--prunepaths=",
|
||||
"--prunefs=",
|
||||
]);
|
||||
|
||||
let mut group = c.benchmark_group("locate");
|
||||
|
||||
// Pattern matching nothing: forces a full decode + match of every entry.
|
||||
group.bench_function("count_no_match", |b| {
|
||||
b.iter(|| run(black_box(&["-d", db_path, "-c", "NONEXISTENT_TOKEN_XYZ"])));
|
||||
});
|
||||
// Substring matching many entries (count mode → no per-match output).
|
||||
group.bench_function("count_many", |b| {
|
||||
b.iter(|| run(black_box(&["-d", db_path, "-c", ".log"])));
|
||||
});
|
||||
// Basename matching (-b) restricts the comparison to the file name.
|
||||
group.bench_function("basename", |b| {
|
||||
b.iter(|| run(black_box(&["-d", db_path, "-c", "-b", "module"])));
|
||||
});
|
||||
// Case-insensitive matching (-i).
|
||||
group.bench_function("ignore_case", |b| {
|
||||
b.iter(|| run(black_box(&["-d", db_path, "-c", "-i", "MODULE"])));
|
||||
});
|
||||
// Regex matching (-r) over the whole path.
|
||||
group.bench_function("regex", |b| {
|
||||
b.iter(|| {
|
||||
run(black_box(&[
|
||||
"-d",
|
||||
db_path,
|
||||
"-c",
|
||||
"-r",
|
||||
r"module_[0-9]+\.rs",
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
group.finish();
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
let _ = std::fs::remove_file(&db);
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_e2e);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
criterion::criterion_main!(bench::benches);
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn main() {}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright 2024 the uutils developers
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
//! End-to-end benchmarks for `updatedb`, run through the real `updatedb_main`
|
||||
//! entry point. Each case walks a generated directory tree (via the internal
|
||||
//! `find`), encodes it into the LOCATE02 database format and writes the result
|
||||
//! to a temp file, so the walk + front-coding + write dominate the measurement.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use findutils::updatedb::updatedb_main;
|
||||
|
||||
/// Run `updatedb` end-to-end. `args` are the arguments after the program name.
|
||||
fn run(args: &[&str]) {
|
||||
let mut argv: Vec<&str> = Vec::with_capacity(args.len() + 1);
|
||||
argv.push("updatedb");
|
||||
argv.extend_from_slice(args);
|
||||
let _ = updatedb_main(&argv);
|
||||
}
|
||||
|
||||
/// Build a directory tree to index. `depth` levels, each holding `dirs`
|
||||
/// sub-directories and `files` files with varied names. Returns the root.
|
||||
fn build_tree(depth: u32, dirs: u32, files: u32) -> PathBuf {
|
||||
let root = std::env::temp_dir().join(format!("uu_updatedb_bench_{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
populate(&root, depth, dirs, files, 0);
|
||||
root
|
||||
}
|
||||
|
||||
fn populate(dir: &Path, depth: u32, dirs: u32, files: u32, seed: u32) {
|
||||
for f in 0..files {
|
||||
let n = seed.wrapping_add(f);
|
||||
let name = match n % 4 {
|
||||
0 => format!("module_{n}.rs"),
|
||||
1 => format!("data_{n}.txt"),
|
||||
2 => format!("image_{n}.png"),
|
||||
_ => format!("file_{n}.log"),
|
||||
};
|
||||
// Tiny files: updatedb only records names, so content size is irrelevant.
|
||||
std::fs::write(dir.join(name), b"x").unwrap();
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
for d in 0..dirs {
|
||||
let sub = dir.join(format!("dir_{d}"));
|
||||
std::fs::create_dir_all(&sub).unwrap();
|
||||
populate(
|
||||
&sub,
|
||||
depth - 1,
|
||||
dirs,
|
||||
files,
|
||||
seed.wrapping_add((d + 1) * 31),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn bench_e2e(c: &mut Criterion) {
|
||||
// depth 4, 4 dirs/level, 25 files/dir → ~8.5k indexed paths.
|
||||
let root = build_tree(4, 4, 25);
|
||||
let dir = root.to_str().unwrap();
|
||||
let db = std::env::temp_dir().join(format!("uu_updatedb_bench_{}.db", std::process::id()));
|
||||
let db_path = db.to_str().unwrap();
|
||||
|
||||
let localpaths = format!("--localpaths={dir}");
|
||||
let output = format!("--output={db_path}");
|
||||
|
||||
let mut group = c.benchmark_group("updatedb");
|
||||
|
||||
// Full build: walk, front-code and write the whole tree. Empty prune
|
||||
// options keep the run deterministic regardless of where temp_dir lives.
|
||||
group.bench_function("build_full", |b| {
|
||||
b.iter(|| {
|
||||
run(black_box(&[
|
||||
&localpaths,
|
||||
&output,
|
||||
"--prunepaths=",
|
||||
"--prunefs=",
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
// Build with a prune clause that drops every `dir_0` subtree, exercising the
|
||||
// -regex/-prune path updatedb hands to find.
|
||||
group.bench_function("build_pruned", |b| {
|
||||
b.iter(|| {
|
||||
run(black_box(&[
|
||||
&localpaths,
|
||||
&output,
|
||||
"--prunepaths=.*/dir_0",
|
||||
"--prunefs=",
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
group.finish();
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
let _ = std::fs::remove_file(&db);
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_e2e);
|
||||
criterion_main!(benches);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user