diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000..52a3772 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,26 @@ +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted + status: {} +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: automerge + uses: "pascalgn/automerge-action@v0.3.2" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_METHOD: "squash" + LABELS: "!work-in-progress" diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 1ae8da0..2464848 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -36,7 +36,7 @@ jobs: name: Test strategy: matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] + os: [ubuntu-latest, macOS-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 @@ -68,7 +68,7 @@ jobs: - name: download cargo-deny shell: bash env: - DVS: 0.3.0-beta + DVS: "0.4.0" DREPO: EmbarkStudios/cargo-deny TARGET: x86_64-unknown-linux-musl run: | @@ -81,7 +81,6 @@ jobs: - name: cargo-deny check bans run: ./cargo-deny -L debug check ban - # Remove this check if you don't publish the crate(s) from this repo publish-check: name: Publish Check runs-on: ubuntu-latest @@ -95,27 +94,15 @@ jobs: uses: actions-rs/cargo@v1 with: command: fetch - - name: copy README - shell: bash - run: | - cp README.md lib - cp README.md cli - - name: cargo publish lib - uses: actions-rs/cargo@v1 - with: - command: publish - args: --dry-run --allow-dirty --manifest-path lib/Cargo.toml - - name: cargo publish cli + - name: cargo publish uses: actions-rs/cargo@v1 with: command: publish - args: --dry-run --allow-dirty --manifest-path cli/Cargo.toml + args: --dry-run - # Remove this job if you don't publish the crate(s) from this repo - # You must add a crates.io API token to your GH secrets called CRATES_IO_TOKEN publish: name: Publish - needs: [test, deny-check, publish-check] + needs: [test, publish-check] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: @@ -135,11 +122,9 @@ jobs: with: command: publish - # Remove this job if you don't release binaries - # Replace occurances of $BIN_NAME with the name of your binary release: name: Release - needs: [test, deny-check] + needs: [test] if: startsWith(github.ref, 'refs/tags/') strategy: matrix: @@ -148,20 +133,15 @@ jobs: - os: ubuntu-latest rust: stable target: x86_64-unknown-linux-musl - bin: about-me - # We don't enable the progress feature when targeting - # musl since there are some dependencies on shared libs - features: "" + bin: cargo-about - os: windows-latest rust: stable target: x86_64-pc-windows-msvc - bin: about-me.exe - features: --features=progress + bin: cargo-about.exe - os: macOS-latest rust: stable target: x86_64-apple-darwin - bin: about-me - features: --features=progress + bin: cargo-about runs-on: ${{ matrix.os }} steps: - name: Install stable toolchain @@ -183,14 +163,13 @@ jobs: args: --target ${{ matrix.target }} - name: Release build uses: actions-rs/cargo@v1 - if: matrix.os != 'ubuntu-latest' with: command: build - args: --release --target ${{ matrix.target }} ${{ matrix.features }} + args: --release --target ${{ matrix.target }} - name: Package shell: bash run: | - name=about-me + name=cargo-about tag=$(git describe --tags --abbrev=0) release_name="$name-$tag-${{ matrix.target }}" release_tar="${release_name}.tar.gz" @@ -217,6 +196,6 @@ jobs: uses: softprops/action-gh-release@v1 with: draft: true - files: 'about-me*' + files: 'cargo-about*' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 6936990..53eaa21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target **/*.rs.bk -Cargo.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index c28e95b..60411b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,29 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.1.1] - 2019-09-03 -### Added -- New features go here in a bullet list - -### Changed -- Changes to existing functionality go here in a bullet list - -### Deprecated -- Mark features soon-to-be removed in a bullet list - -### Removed -- Features that have been removed in a bullet list - -### Fixed -- Bug fixes in a bullet list - -### Security -- Changes/fixes related to security vulnerabilities in a bullet list - -## [0.1.0] - 2019-09-02 +## [0.0.1] - 2019-11-07 ### Added - Initial add of the thing -[Unreleased]: https://github.com/EmbarkStudios/about-me/compare/0.1.1...HEAD -[0.1.1]: https://github.com/EmbarkStudios/about-me/compare/0.1.0...0.1.1 -[0.1.0]: https://github.com/EmbarkStudios/about-me/releases/tag/0.1.0 +[Unreleased]: https://github.com/EmbarkStudios/cargo-about/compare/0.0.1...HEAD +[0.0.1]: https://github.com/EmbarkStudios/cargo-about/releases/tag/0.0.1 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a8f5c46 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,960 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "anyhow" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "askalono" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cargo-about" +version = "0.0.1" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "askalono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spdx 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cargo_metadata" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fern" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "handlebars" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hashbrown" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "jobserver" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memoffset" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pest" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_generator" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_meta" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-error" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rmp" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rmp-serde" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha-1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smallvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "spdx" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ucd-trie" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-normalization" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-segmentation" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zstd" +version = "0.4.28+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-safe" +version = "1.4.13+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zstd-sys" +version = "1.4.13+zstd.1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum anyhow 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "57114fc2a6cc374bce195d3482057c846e706d252ff3604363449695684d7a0d" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +"checksum askalono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f772f9f245cb9d66ab3f8d1198ed73e8c29a21ed2c21d104157d2e7eb4fcf5d" +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d" +"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +"checksum handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "91ef1ac30f2eaaa2b835fce73c57091cb6b9fc62b7eef285efbf980b0f20001b" +"checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b1d42ef453b30b7387e113da1c83ab1605d90c5b4e0eb8e96d016ed3b8c160" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a85c1a8c329f11437034d7313dca647c79096523533a1c79e86f1d0f657c7cc" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "155394f924cdddf08149da25bfb932d226b4a593ca7468b08191ff6335941af5" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +"checksum pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +"checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" +"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +"checksum rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f594cb7ff8f1c5a7907f6be91f15795c8301e0d5718eb007fb5832723dd716e" +"checksum rmp-serde 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" +"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" +"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" +"checksum spdx 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4348fc0f87adf1ccbfc0bb009afb4620a46a2f8d1bdac2d9314a63169816d57" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4f66a4c0ddf7aee4677995697366de0749b0139057342eccbb609b12d0affc" +"checksum structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fe0c13e476b4e21ff7f5c4ace3818b6d7bdc16897c31c73862471bc1663acae" +"checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" +"checksum synstructure 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "575be94ccb86e8da37efb894a87e2b660be299b41d8ef347f9d6d79fbe61b1ba" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" +"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" +"checksum unicode-segmentation 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49f5526225fd8b77342d5986ab5f6055552e9c0776193b5b63fd53b46debfad7" +"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4e716acaad66f2daf2526f37a1321674a8814c0b37a366ebe6c97a699f85ddc" +"checksum zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfe4d3b26a0790201848865663e8ffabf091e126e548bc9710ccfa95621ece48" +"checksum zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fadc8ebe858f056ab82dffb9d93850b841603bdf663db7cf5e3dbd7f34cc55b2" diff --git a/Cargo.toml b/Cargo.toml index f677c17..f135378 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,38 @@ [package] -name = "about-me" -version = "0.1.0" +name = "cargo-about" +version = "0.0.1" +description = "Cargo plugin for generating a listing of all of the crates and the the terms under which they are licensed" authors = ["Embark ", "Jake Shadle "] edition = "2018" +repository = "https://github.com/EmbarkStudios/cargo-about" +license = "MIT OR Apache-2.0" +readme = "README.md" +documentation = "https://docs.rs/cargo-about" +homepage = "https://github.com/EmbarkStudios/cargo-about" +categories = ["development-tools::cargo-plugins"] +keywords = ["cargo", "license", "spdx"] + +[badges] +maintenance = { status = "actively-developed" } + +[[bin]] +name = "cargo-about" +path = "src/cargo-about/main.rs" [dependencies] +ansi_term = "0.11.0" +anyhow = "1.0.18" +askalono = "0.4.0" +cargo_metadata = "0.9.0" +chrono = "0.4.9" +fern = "0.5.9" +handlebars = "2.0.2" +log = "0.4.8" +rayon = "1.2.0" +serde = { version = "1.0.102", features = ["derive"] } +serde_json = "1.0.41" +smallvec = "1.0.0" +spdx = "0.2.3" +structopt = "0.3.3" +toml = "0.5.5" +walkdir = "2.2.9" diff --git a/README.md b/README.md index a86715c..c25afd3 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# 📜 about-me +# 📜 cargo-about -[![Build Status](https://github.com/EmbarkStudios/template/workflows/CI/badge.svg)](https://github.com/EmbarkStudios/about-me/actions?workflow=CI) -[![Crates.io](https://img.shields.io/crates/v/about-me.svg)](https://crates.io/crates/about-me) -[![Docs](https://docs.rs/about-me/badge.svg)](https://docs.rs/about-me) +[![Build Status](https://github.com/EmbarkStudios/template/workflows/CI/badge.svg)](https://github.com/EmbarkStudios/cargo-about/actions?workflow=CI) +[![Crates.io](https://img.shields.io/crates/v/cargo-about.svg)](https://crates.io/crates/cargo-about) +[![Docs](https://docs.rs/cargo-about/badge.svg)](https://docs.rs/cargo-about) [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) [![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](https://embark.dev) -Cargo plugin for generating a listing of all of the crates and the the terms under which they are licensed. +Cargo plugin for generating a listing of all of the crates used by a root crate, and the the terms under which they are licensed. ## TODO: diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..cbc8ea6 --- /dev/null +++ b/deny.toml @@ -0,0 +1,22 @@ +[bans] +multiple-versions = "deny" +deny = [ + # You can never be too sure + { name = "openssl" }, +] +skip = [ + # unicode-normalization uses and old smallvec + { name = "smallvec", version = "=0.6.13" }, +] + +[licenses] +unlicensed = "deny" +unknown = "deny" +allow-osi-fsf-free = "neither" +copyleft = "deny" +# We want really high confidence when inferring licenses from text +confidence-threshold = 0.93 +allow = [ + "Apache-2.0", + "MIT", +] diff --git a/spdx_cache.bin.zstd b/spdx_cache.bin.zstd new file mode 100644 index 0000000..addae16 Binary files /dev/null and b/spdx_cache.bin.zstd differ diff --git a/src/cargo-about/generate.rs b/src/cargo-about/generate.rs new file mode 100644 index 0000000..4bd85aa --- /dev/null +++ b/src/cargo-about/generate.rs @@ -0,0 +1,257 @@ +use anyhow::{bail, Context, Error}; +use cargo_about::{licenses, Krate, Krates}; +use handlebars::Handlebars; +use serde::Serialize; +use std::{collections::HashMap, path::PathBuf}; +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +pub struct Args { + /// The confidence threshold required for license files + /// to be positively identified: 0.0 - 1.0 + #[structopt(long, default_value = "0.8")] + threshold: f32, + /// Include local crates beneath one or more directories, local crates + /// are disregarded by default + #[structopt(short, long)] + include_local: Vec, + /// The name of the template to use when rendering. If only passing a + /// single template file to `templates` this is not used. + #[structopt(short, long)] + name: Option, + /// The template or template directory to use. Must either be a .hbs or + /// have at least + #[structopt(parse(from_os_str))] + templates: PathBuf, +} + +pub fn cmd( + args: Args, + cfg: licenses::config::Config, + krates: Krates, + store: licenses::LicenseStore, +) -> Result<(), Error> { + let (registry, template) = { + let mut reg = Handlebars::new(); + + if !args.templates.exists() { + bail!( + "template(s) path {} does not exist", + args.templates.display() + ); + } + + use handlebars::*; + + reg.register_helper( + "json", + Box::new( + |h: &Helper<'_, '_>, + _r: &Handlebars, + _: &Context, + _rc: &mut RenderContext<'_>, + out: &mut dyn Output| + -> HelperResult { + let param = h + .param(0) + .ok_or_else(|| RenderError::new("param not found"))?; + + out.write(&serde_json::to_string_pretty(param.value())?)?; + Ok(()) + }, + ), + ); + + // reg.register_helper( + // "sanitize-html", + // Box::new( + // |h: &Helper, + // r: &Handlebars, + // _: &Context, + // rc: &mut RenderContext<'_>, + // out: &mut dyn Output| + // -> HelperResult { + // let param = h.param(0).ok_or(RenderError::new("param not found"))?; + + // let val = param + // .value() + // .as_str() + // .ok_or_else(|| RenderError::new("expected string"))?; + + // let cleaned = ammonia::clean(val); + + // if val != cleaned { + // println!("{}", difference::Changeset::new(val, &cleaned, "")); + // //return Err(RenderError::new("HOLY CRAP WE DID IT")); + // } + // out.write(&cleaned)?; + // Ok(()) + // }, + // ), + // ); + + if args.templates.is_dir() { + reg.register_templates_directory(".hbs", &args.templates)?; + + if reg.get_templates().is_empty() { + bail!( + "template path {} did not contain any hbs files", + args.templates.display() + ); + } + + (reg, args.name.context("specified a directory for templates, but did not provide the name of the template to use")?) + } else { + // Ignore the extension, if the user says they want to use a specific file, that's on them + reg.register_template_file("tmpl", args.templates)?; + + (reg, "tmpl".to_owned()) + } + }; + + let mut summary = licenses::Gatherer::with_store(std::sync::Arc::new(store)) + .with_confidence_threshold(args.threshold) + .gather(&krates.krates, &cfg); + + licenses::sanitize(&mut summary)?; + + let output = generate(&summary, ®istry, &template)?; + + println!("{}", output); + + Ok(()) +} + +#[derive(Serialize)] +struct UsedBy<'a> { + #[serde(rename = "crate")] + krate: &'a Krate, + path: Option, +} + +#[derive(Serialize)] +struct License<'a> { + /// The full name of the license + name: String, + /// The SPDX short identifier for the license + id: String, + /// The full license text + text: String, + /// The path where the license text was sourced from + source_path: PathBuf, + /// The list of crates this license was applied to + used_by: Vec>, +} + +#[derive(Serialize)] +struct LicenseSet { + count: usize, + name: String, + id: String, + indices: Vec, +} + +#[derive(Serialize)] +struct Input<'a> { + overview: Vec, + licenses: Vec>, +} + +fn generate( + summary: &licenses::Summary<'_>, + hbs: &Handlebars, + template_name: &str, +) -> Result { + let licenses = { + let mut licenses = HashMap::new(); + + for k in &summary.nfos { + if let licenses::LicenseInfo::Expr(ref expr) = k.lic_info { + for req in expr.requirements().filter_map(|r| { + if let spdx::LicenseItem::SPDX { id, .. } = r.req.license { + Some(id) + } else { + None + } + }) { + if let Some(nfo) = k.license_files.iter().find(|lf| { + if lf.id != req { + return false; + } + + match lf.info { + licenses::LicenseFileInfo::Text(_) + | licenses::LicenseFileInfo::AddendumText(_, _) => true, + _ => false, + } + }) { + let entry = licenses.entry(req.name).or_insert_with(HashMap::new); + + let contents = match nfo.info { + licenses::LicenseFileInfo::Text(ref s) => s, + licenses::LicenseFileInfo::AddendumText(ref s, _) => s, + _ => unreachable!(), + }; + + let lic = entry.entry(contents).or_insert_with(|| License { + name: req.full_name.to_owned(), + id: req.name.to_owned(), + text: contents.clone(), + used_by: Vec::new(), + source_path: nfo.path.clone(), + }); + + match nfo.info { + licenses::LicenseFileInfo::Text(_) => { + lic.used_by.push(UsedBy { + krate: k.krate, + path: None, + }); + } + licenses::LicenseFileInfo::AddendumText(_, ref p) => { + lic.used_by.push(UsedBy { + krate: k.krate, + path: Some(p.clone()), + }) + } + _ => unreachable!(), + } + } + } + } + } + + let mut licenses = licenses + .into_iter() + .flat_map(|(_, v)| v.into_iter().map(|(_, v)| v)) + .collect::>(); + + licenses.sort_by(|a, b| a.id.cmp(&b.id)); + licenses + }; + + let mut overview: Vec = Vec::with_capacity(256); + + for (ndx, lic) in licenses.iter().enumerate() { + match overview.binary_search_by(|i| i.id.cmp(&lic.id)) { + Ok(i) => overview[i].indices.push(ndx), + Err(i) => { + let mut ls = LicenseSet { + count: 0, + name: lic.name.clone(), + id: lic.id.clone(), + indices: Vec::with_capacity(10), + }; + + ls.indices.push(ndx); + overview.insert(i, ls); + } + } + } + + overview.iter_mut().for_each(|i| i.count = i.indices.len()); + + let nput = Input { licenses, overview }; + + Ok(hbs.render(template_name, &nput)?) +} diff --git a/src/cargo-about/main.rs b/src/cargo-about/main.rs new file mode 100644 index 0000000..1923a60 --- /dev/null +++ b/src/cargo-about/main.rs @@ -0,0 +1,163 @@ +#![warn(clippy::all)] +#![warn(rust_2018_idioms)] + +use anyhow::{anyhow, bail, Context, Error}; +use std::path::{Path, PathBuf}; +use structopt::StructOpt; + +mod generate; + +#[derive(StructOpt, Debug)] +enum Command { + /// Outputs a listing of all licenses and the crates that use them + #[structopt(name = "generate")] + Generate(generate::Args), +} + +fn parse_level(s: &str) -> Result { + s.parse::() + .map_err(|e| anyhow!("failed to parse level '{}': {}", s, e)) +} + +#[derive(Debug, StructOpt)] +struct Opts { + /// The log level for messages, only log messages at or above + /// the level will be emitted. + #[structopt( + short = "L", + long = "log-level", + default_value = "warn", + parse(try_from_str = parse_level), + long_help = "The log level for messages, only log messages at or above the level will be emitted. + +Possible values: +* off +* error +* warn +* info +* debug +* trace" + )] + log_level: log::LevelFilter, + /// The path of the Cargo.toml to use + #[structopt(short, long = "manifest-path", parse(from_os_str))] + manifest_path: Option, + #[structopt(subcommand)] + cmd: Command, +} + +fn setup_logger(level: log::LevelFilter) -> Result<(), fern::InitError> { + use ansi_term::Color::*; + use log::Level::*; + + fern::Dispatch::new() + .level(log::LevelFilter::Warn) + .level_for("cargo_about", level) + .format(move |out, message, record| { + out.finish(format_args!( + "{date} [{level}] {message}\x1B[0m", + date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S"), + level = match record.level() { + Error => Red.paint("ERROR"), + Warn => Yellow.paint("WARN"), + Info => Green.paint("INFO"), + Debug => Blue.paint("DEBUG"), + Trace => Purple.paint("TRACE"), + }, + message = message, + )); + }) + .chain(std::io::stderr()) + .apply()?; + Ok(()) +} + +fn load_config(manifest_path: &Path) -> Result { + let mut parent = manifest_path.parent(); + + // Move up directories until we find an about.toml, to handle + // cases where eg in a workspace there is a top-level about.toml + // but the user is only getting a listing for a particular crate from it + while let Some(p) = parent { + if !p.join("Cargo.toml").exists() { + break; + } + + let about_toml = p.join("about.toml"); + + if about_toml.exists() { + let contents = std::fs::read_to_string(&about_toml)?; + let cfg = toml::from_str(&contents)?; + + log::info!("loaded config from {}", about_toml.display()); + return Ok(cfg); + } + + parent = p.parent(); + } + + Ok(cargo_about::licenses::config::Config::default()) +} + +fn real_main() -> Result<(), Error> { + let args = Opts::from_iter({ + std::env::args().enumerate().filter_map(|(i, a)| { + if i == 1 && a == "about" { + None + } else { + Some(a) + } + }) + }); + + setup_logger(args.log_level)?; + + let manifest_path = args + .manifest_path + .clone() + .or_else(|| { + std::env::current_dir() + .and_then(|cd| Ok(cd.join("Cargo.toml"))) + .ok() + }) + .context("unable to determine manifest path")?; + + if !manifest_path.exists() { + bail!( + "cargo manifest path '{}' does not exist", + manifest_path.display() + ); + } + + let cfg = load_config(&manifest_path)?; + + let (all_crates, store) = rayon::join( + || { + log::info!("gathering crates for {}", manifest_path.display()); + cargo_about::get_all_crates(&manifest_path) + }, + || { + log::info!("loading license store"); + cargo_about::licenses::LicenseStore::from_cache() + }, + ); + + let all_crates = all_crates?; + let store = store?; + + log::info!("gathered {} crates", all_crates.krates.len()); + + match args.cmd { + Command::Generate(gen) => generate::cmd(gen, cfg, all_crates, store), + } +} + +fn main() { + match real_main() { + Ok(_) => {} + Err(e) => { + log::error!("{}", e); + std::process::exit(1); + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..67f648b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,55 @@ +use anyhow::{Context, Error}; +use std::{collections::HashMap, path::Path}; + +pub type Pid = cargo_metadata::PackageId; +pub type Krate = cargo_metadata::Package; + +pub mod licenses; + +pub struct Krates { + pub krates: Vec, + pub krate_map: HashMap, + pub resolved: cargo_metadata::Resolve, + //pub lock_file: PathBuf, +} + +pub fn get_all_crates>(cargo_toml: P) -> Result { + use rayon::prelude::*; + + let metadata = cargo_metadata::MetadataCommand::new() + .manifest_path(cargo_toml) + .features(cargo_metadata::CargoOpt::AllFeatures) + .exec() + .context("failed to fetch metadata")?; + + let mut krates = metadata.packages; + + for krate in &mut krates { + if let Some(ref mut lf) = krate.license { + *lf = lf.replace("/", " OR "); + } + } + + krates.par_sort_by(|a, b| a.id.cmp(&b.id)); + + let map = krates + .iter() + .enumerate() + .map(|(i, ci)| (ci.id.clone(), i)) + .collect(); + + let mut resolved = metadata.resolve.unwrap(); + + resolved.nodes.par_sort_by(|a, b| a.id.cmp(&b.id)); + resolved + .nodes + .par_iter_mut() + .for_each(|nodes| nodes.dependencies.par_sort()); + + Ok(Krates { + krates, + krate_map: map, + resolved, + //lock_file: root.as_ref().join("Cargo.lock"), + }) +} diff --git a/src/licenses/config.rs b/src/licenses/config.rs new file mode 100644 index 0000000..c355b82 --- /dev/null +++ b/src/licenses/config.rs @@ -0,0 +1,67 @@ +use serde::{de, Deserialize}; +use std::{collections::BTreeMap, fmt, path::PathBuf}; + +fn deserialize_spdx_id<'de, D>(deserializer: D) -> std::result::Result +where + D: de::Deserializer<'de>, +{ + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = spdx::LicenseId; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("SPDX short-identifier") + } + + fn visit_str(self, v: &str) -> std::result::Result + where + E: de::Error, + { + spdx::license_id(v).ok_or_else(|| { + E::custom(format!( + "'{}' is not a valid SPDX short-identifier in v{}", + v, + spdx::license_version() + )) + }) + } + } + + deserializer.deserialize_any(Visitor) +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Additional { + pub root: PathBuf, + #[serde(deserialize_with = "deserialize_spdx_id")] + pub license: spdx::LicenseId, + pub license_file: PathBuf, + pub license_start: Option, + pub license_end: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Ignore { + #[serde(deserialize_with = "deserialize_spdx_id")] + pub license: spdx::LicenseId, + pub license_file: PathBuf, + pub license_start: Option, + pub license_end: Option, +} + +#[derive(Deserialize, Debug)] +pub struct KrateConfig { + #[serde(default)] + pub additional: Vec, + #[serde(default)] + pub ignore: Vec, +} + +#[derive(Deserialize, Debug, Default)] +pub struct Config { + #[serde(flatten)] + pub inner: BTreeMap, +} diff --git a/src/licenses/mod.rs b/src/licenses/mod.rs new file mode 100644 index 0000000..fbb561c --- /dev/null +++ b/src/licenses/mod.rs @@ -0,0 +1,582 @@ +use crate::Krate; +use anyhow::{bail, Error}; +use rayon::prelude::*; +use spdx::LicenseId; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; + +pub mod config; + +const LICENSE_CACHE: &[u8] = include_bytes!("../../spdx_cache.bin.zstd"); + +pub struct LicenseStore { + store: askalono::Store, +} + +impl LicenseStore { + pub fn from_cache() -> Result { + let store = askalono::Store::from_cache(LICENSE_CACHE) + .map_err(|e| anyhow::anyhow!("failed to load license store: {}", e))?; + + Ok(Self { store }) + } +} + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum LicenseInfo { + Expr(spdx::Expression), + Unknown, +} + +/// The contents of a file with license info in it +pub enum LicenseFileInfo { + /// The license file is the canonical text of the license + Text(String), + /// The license file is the canonical text, and applies to + /// a path root + AddendumText(String, PathBuf), + /// The file just has a license header, and presumably + /// also contains other text in it (like, you know, code) + Header, +} + +impl LicenseFileInfo { + fn is_addendum(&self) -> bool { + if let LicenseFileInfo::AddendumText(_, _) = self { + true + } else { + false + } + } +} + +pub struct LicenseFile { + /// The SPDX identifier for the license in the file + pub id: LicenseId, + /// Full path of the file which had license data in it + pub path: PathBuf, + /// The confidence score for the license, the closer to the canonical + /// license text it is, the closert it approaches 1.0 + pub confidence: f32, + /// The contents of the file + pub info: LicenseFileInfo, +} + +pub struct KrateLicense<'a> { + pub krate: &'a Krate, + pub lic_info: LicenseInfo, + pub license_files: Vec, +} + +pub struct Summary<'a> { + store: Arc, + pub nfos: Vec>, +} + +impl<'a> Summary<'a> { + fn new(store: Arc) -> Self { + Self { + store, + nfos: Vec::new(), + } + } +} + +pub struct Gatherer { + store: Arc, + threshold: f32, +} + +impl Gatherer { + pub fn with_store(store: Arc) -> Self { + Self { + store, + threshold: 0.8, + } + } + + pub fn with_confidence_threshold(mut self, threshold: f32) -> Self { + self.threshold = if threshold > 1.0 { + 1.0 + } else if threshold < 0.0 { + 0.0 + } else { + threshold + }; + self + } + + pub fn gather<'k>(self, krates: &'k [crate::Krate], cfg: &config::Config) -> Summary<'k> { + let mut summary = Summary::new(self.store); + + let threshold = self.threshold; + let min_threshold = threshold - 0.5; + + let strategy = askalono::ScanStrategy::new(&summary.store.store) + .mode(askalono::ScanMode::Elimination) + .confidence_threshold(if min_threshold < 0.1 { + 0.1 + } else { + min_threshold + }) + .optimize(false) + .max_passes(1); + + krates + .par_iter() + .map(|krate| { + let info = match krate.license { + Some(ref license_field) => { + //. Reasons this can fail: + // * Empty! The rust crate used to validate this field has a bug + // https://github.com/rust-lang-nursery/license-exprs/issues/23 + // * It also just does basic lexing, so parens, duplicate operators, + // unpaired exceptions etc can all fail validation + + match spdx::Expression::parse(license_field) { + Ok(validated) => LicenseInfo::Expr(validated), + Err(err) => { + log::error!( + "unable to parse license expression for '{} - {}': {}", + krate.name, + krate.version, + err + ); + LicenseInfo::Unknown + } + } + } + None => { + log::warn!( + "crate '{}({})' doesn't have a license field", + krate.name, + krate.version, + ); + LicenseInfo::Unknown + } + }; + + let root_path = krate.manifest_path.parent().unwrap(); + let krate_cfg = cfg.inner.get(&krate.name); + + let license_files = match scan_files( + &root_path, + &strategy, + threshold, + krate_cfg.map(|kc| (kc, krate.name.as_str())), + ) { + Ok(files) => files, + Err(err) => { + log::error!( + "unable to scan for license files for crate '{} - {}': {}", + krate.name, + krate.version, + err + ); + + Vec::new() + } + }; + + KrateLicense { + krate, + lic_info: info, + license_files, + } + }) + .collect_into_vec(&mut summary.nfos); + + summary + } +} + +fn is_ignored(entry: &walkdir::DirEntry) -> bool { + entry + .file_name() + .to_str() + .map(|s| { + // Ignore hidden directories + if s.starts_with('.') { + log::debug!("ignoring hidden directory {}", entry.path().display()); + return true; + } + + // Include typical files + if entry.file_type().is_file() { + if s.starts_with("LICENSE") { + return false; + } + + // Filter out typical binary files + if let Some(ext) = entry.path().extension() { + return match ext.to_string_lossy().as_ref() { + // Binary artifacts + "a" | "o" | "lib" | "obj" | "pyc" | "dll" | "exe" | "so" => true, + // Binary sources + "ttf" | "ico" | "dfa" | "rc" => true, + // Test data + "png" | "spv" | "vert" | "wasm" | "zip" | "gz" | "wav" | "jpg" | "bin" + | "zlib" | "p8" | "deflate" => true, + // Misc binary + "der" | "metallib" | "pdf" => true, + _ => false, + }; + } + } + + false + }) + .unwrap_or(false) +} + +fn scan_files( + root_dir: &Path, + strat: &askalono::ScanStrategy, + threshold: f32, + krate_cfg: Option<(&config::KrateConfig, &str)>, +) -> Result, Error> { + use walkdir::WalkDir; + + let walker = WalkDir::new(root_dir).into_iter(); + + let files: Vec<_> = walker + .filter_entry(|e| !is_ignored(e)) + .filter_map(|e| e.ok()) + .collect(); + + let license_files: Vec<_> = files + .into_par_iter() + .filter_map(|file| { + log::trace!("scanning file {}", file.path().display()); + + if file.file_type().is_dir() { + return None; + } + + let mut contents = match read_file(file.path()) { + Some(c) => c, + None => return None, + }; + + let expected = match krate_cfg { + Some(krate_cfg) => { + let relative = match file.path().strip_prefix(root_dir) { + Ok(rel) => rel, + Err(_) => return None, + }; + + match krate_cfg + .0 + .ignore + .iter() + .find(|i| relative == i.license_file) + { + Some(ignore) => { + contents = + snip_contents(contents, ignore.license_start, ignore.license_end); + Some((ignore.license, None)) + } + None => { + let mut addendum = None; + + for additional in &krate_cfg.0.additional { + if relative == additional.license_file { + addendum = Some(additional); + break; + } + + if relative.starts_with(&additional.root) { + log::trace!( + "skipping {} due to addendum for root {}", + file.path().display(), + additional.root.display() + ); + return None; + } + } + + addendum + .map(|addendum| (addendum.license, Some(&addendum.license_file))) + } + } + } + None => None, + }; + + let path = file.into_path(); + + match scan_text(&contents, strat, threshold) { + ScanResult::Header(ided) => { + if let Some((exp_id, addendum)) = expected { + if exp_id != ided.id { + log::error!( + "expected license '{}' in path '{}', but found '{}'", + exp_id.name, + path.display(), + ided.id.name + ); + } else if addendum.is_none() { + log::debug!( + "ignoring '{}', matched license '{}'", + path.display(), + ided.id.name + ); + return None; + } + } + + Some(LicenseFile { + id: ided.id, + confidence: ided.confidence, + path, + info: LicenseFileInfo::Header, + }) + } + ScanResult::Text(ided) => { + let info = if let Some((exp_id, addendum)) = expected { + if exp_id != ided.id { + log::error!( + "expected license '{}' in path '{}', but found '{}'", + exp_id.name, + path.display(), + ided.id.name + ); + } + + match addendum { + Some(path) => LicenseFileInfo::AddendumText(contents, path.clone()), + None => { + log::debug!( + "ignoring '{}', matched license '{}'", + path.display(), + ided.id.name + ); + return None; + } + } + } else { + LicenseFileInfo::Text(contents) + }; + + Some(LicenseFile { + id: ided.id, + confidence: ided.confidence, + path, + info, + }) + } + ScanResult::UnknownId(id_str) => { + log::error!( + "found unknown SPDX identifier '{}' scanning '{}'", + id_str, + path.display() + ); + None + } + ScanResult::LowLicenseChance(ided) => { + log::debug!( + "found '{}' scanning '{}' but it only has a confidence score of {}", + ided.id.name, + path.display(), + ided.confidence + ); + None + } + ScanResult::NoLicense => None, + } + }) + .collect(); + + Ok(license_files) +} + +fn read_file(path: &Path) -> Option { + match std::fs::read_to_string(path) { + Err(ref e) if e.kind() == std::io::ErrorKind::InvalidData => { + // If we fail due to invaliddata, it just means the file in question was + // probably binary and didn't have valid utf-8 data, so we can ignore it + log::debug!("binary file {} detected", path.display()); + None + } + Err(e) => { + log::error!("failed to read '{}': {}", path.display(), e); + None + } + Ok(c) => Some(c), + } +} + +fn snip_contents(contents: String, start: Option, end: Option) -> String { + let rng = start.unwrap_or(0)..end.unwrap_or(std::usize::MAX); + + if rng.start == 0 && rng.end == std::usize::MAX { + contents + } else { + let mut snipped_contents = String::with_capacity(contents.len()); + for (i, line) in contents.lines().enumerate() { + if i >= rng.start && i < rng.end { + snipped_contents.push_str(line); + snipped_contents.push('\n'); + } + } + + snipped_contents + } +} + +struct Identified { + confidence: f32, + id: spdx::LicenseId, +} + +enum ScanResult { + Header(Identified), + Text(Identified), + UnknownId(String), + LowLicenseChance(Identified), + NoLicense, +} + +fn scan_text(contents: &str, strat: &askalono::ScanStrategy, threshold: f32) -> ScanResult { + let text = askalono::TextData::new(&contents); + match strat.scan(&text) { + Ok(lic_match) => { + match lic_match.license { + Some(identified) => { + let lic_id = match spdx::license_id(&identified.name) { + Some(id) => Identified { + confidence: lic_match.score, + id, + }, + None => return ScanResult::UnknownId(identified.name.to_owned()), + }; + + // askalano doesn't report any matches below the confidence threshold + // but we want to see what it thinks the license is if the confidence + // is somewhat ok at least + if lic_match.score >= threshold { + match identified.kind { + askalono::LicenseType::Header => ScanResult::Header(lic_id), + askalono::LicenseType::Original => ScanResult::Text(lic_id), + askalono::LicenseType::Alternate => { + unimplemented!("I guess askalono uses this now") + } + } + } else { + ScanResult::LowLicenseChance(lic_id) + } + } + None => ScanResult::NoLicense, + } + } + Err(e) => { + // the elimination strategy can't currently fail + unimplemented!( + "I guess askalano's elimination strategy can now fail: {}", + e + ); + } + } +} + +pub fn sanitize(summary: &mut Summary) -> Result<(), Error> { + let num_errors = summary + .nfos + .par_iter_mut() + .fold( + || 0, + |acc, krate_license| { + // Check that the licenses found by scanning the crate contents match what was stated + // in the license expression + match krate_license.lic_info { + LicenseInfo::Expr(ref expr) => { + let spdx_reqs = expr + .requirements() + .filter_map(|req| { + if let spdx::LicenseItem::SPDX { id, .. } = req.req.license { + Some(id) + } else { + None + } + }) + .collect::>(); + + log::info!( + "crate {}({}) has license(s) {:?} in its `license` field", + krate_license.krate.name, + krate_license.krate.version, + spdx_reqs + ); + + for lf in &krate_license.license_files { + if !lf.info.is_addendum() && !spdx_reqs.contains(&lf.id) { + log::warn!( + "mismatching license found for {}: license '{}' in path '{}'", + krate_license.krate.name, + lf.id.name, + lf.path.display() + ); + } + } + + acc + } + LicenseInfo::Unknown => { + let mut found = smallvec::SmallVec::<[(LicenseId, u32); 2]>::new(); + + for lf in &krate_license.license_files { + match found.iter_mut().find(|lic| lic.0 == lf.id) { + Some(lic) => lic.1 += 1, + None => found.push((lf.id, 1)), + } + } + + let expr_s = { + let mut expr_s = String::new(); + + for (i, name) in found.iter().map(|l| l.0.name).enumerate() { + if i > 0 { + expr_s.push_str(" AND "); + } + + expr_s.push_str(name); + } + + expr_s + }; + + if found.is_empty() { + log::error!("unable to find any license files for crate {}({})", krate_license.krate.name, krate_license.krate.version); + return acc + 1; + } + + let expr = match spdx::Expression::parse(&expr_s) { + Ok(e) => e, + Err(e) => { + log::error!("failed to parse SPDX license expression from synthesized string '{}': {}", expr_s, e); + return acc + 1; + } + }; + + log::warn!("crate {}({}) had no license field, now using SPDX license expression '{}'", krate_license.krate.name, krate_license.krate.version, expr); + krate_license.lic_info = LicenseInfo::Expr(expr); + + acc + }, + } + }, + ) + .sum::(); + + if num_errors > 0 { + bail!( + "encountered {} error sanity checking crate licenses", + num_errors + ); + } else { + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}