From 9d86fe5d1e3e30df9f0a558c2463da03ce6e1c30 Mon Sep 17 00:00:00 2001 From: tison Date: Tue, 11 Jun 2024 11:09:45 +0800 Subject: [PATCH] feat: resolve git file attrs (#150) Signed-off-by: tison --- .cargo/config.toml | 3 + .editorconfig | 3 + .../actions/docker-push-by-digest/action.yml | 3 + .github/actions/docker-release/action.yml | 3 + .github/semantic.yml | 3 + .github/workflows/ci.yml | 12 +- .github/workflows/docker.yml | 3 + Cargo.lock | 302 +++++++++++++++--- Cargo.toml | 3 + Dockerfile | 3 + README.md | 11 +- action.yml | 3 + cli/Cargo.toml | 3 + cli/build.rs | 3 + cli/src/cli.rs | 3 + cli/src/main.rs | 3 + fmt/Cargo.toml | 10 +- fmt/src/config/mod.rs | 18 +- fmt/src/document/defaults.toml | 3 + fmt/src/document/factory.rs | 50 ++- fmt/src/document/mod.rs | 52 +-- fmt/src/document/model.rs | 3 + fmt/src/error.rs | 11 + fmt/src/git.rs | 128 +++++++- fmt/src/header/defaults.toml | 3 + fmt/src/header/matcher.rs | 3 + fmt/src/header/mod.rs | 3 + fmt/src/header/model.rs | 3 + fmt/src/header/parser.rs | 12 +- fmt/src/lib.rs | 3 + fmt/src/license/mod.rs | 3 + fmt/src/processor.rs | 50 +-- fmt/src/selection.rs | 26 +- fmt/tests/tests.rs | 14 +- licenserc.toml | 27 +- rust-toolchain.toml | 3 + rustfmt.toml | 3 + tests/it.py | 3 + 38 files changed, 656 insertions(+), 139 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 77507dc..22a2e63 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 # See also https://github.com/rust-lang/rust/issues/44991 diff --git a/.editorconfig b/.editorconfig index 8ec2430..8108462 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 root = true diff --git a/.github/actions/docker-push-by-digest/action.yml b/.github/actions/docker-push-by-digest/action.yml index f8be373..fea6ae3 100644 --- a/.github/actions/docker-push-by-digest/action.yml +++ b/.github/actions/docker-push-by-digest/action.yml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 name: Docker push by digest description: Build and push Docker image by digest diff --git a/.github/actions/docker-release/action.yml b/.github/actions/docker-release/action.yml index 76ab29a..2d4bd1b 100644 --- a/.github/actions/docker-release/action.yml +++ b/.github/actions/docker-release/action.yml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 name: Docker release description: Release Docker Images diff --git a/.github/semantic.yml b/.github/semantic.yml index 3aafb9d..68a6c08 100644 --- a/.github/semantic.yml +++ b/.github/semantic.yml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 # The pull request's title should be fulfilled the following pattern: # diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc817a9..6cf8ddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 name: CI on: @@ -38,7 +41,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: korandoru/hawkeye@v4 - uses: Swatinem/rust-cache@v2 - uses: dtolnay/rust-toolchain@nightly - name: Check Clippy @@ -62,11 +64,13 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, buildjet-4vcpu-ubuntu-2204-arm ] + os: [ ubuntu-latest ] runs-on: ${{matrix.os}} name: Docker sanity check on ${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Build and load uses: docker/build-push-action@v5 with: @@ -97,6 +101,8 @@ jobs: name: Smoke test on ${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: Swatinem/rust-cache@v2 - name: Dog feed license check shell: bash @@ -119,6 +125,8 @@ jobs: name: Smoke test for GitHub Actions on ${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Check and fail if unknown uses: ./ with: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 886f32c..eee72cf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 name: Release diff --git a/Cargo.lock b/Cargo.lock index 1d5b0a4..4ae19c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -83,6 +84,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "arc-swap" version = "1.7.1" @@ -230,6 +237,19 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "deranged" version = "0.3.11" @@ -245,6 +265,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -269,9 +298,9 @@ checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "filetime" @@ -310,19 +339,33 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gix" -version = "0.61.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e0e59a44bf00de058ee98d6ecf3c9ed8f8842c1da642258ae4120d41ded8f7" +checksum = "984c5018adfa7a4536ade67990b3ebc6e11ab57b3d6cd9968de0947ca99b4b06" dependencies = [ "gix-actor", + "gix-attributes", + "gix-command", "gix-commitgraph", "gix-config", "gix-date", "gix-diff", "gix-discover", "gix-features", + "gix-filter", "gix-fs", "gix-glob", "gix-hash", @@ -335,11 +378,13 @@ dependencies = [ "gix-odb", "gix-pack", "gix-path", + "gix-pathspec", "gix-ref", "gix-refspec", "gix-revision", "gix-revwalk", "gix-sec", + "gix-submodule", "gix-tempfile", "gix-trace", "gix-traverse", @@ -355,9 +400,9 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3a3bde455ad2ee8ba8a195745241ce0b770a8a26faae59fcf409d01b28c46" +checksum = "d69c59d392c7e6c94385b6fd6089d6df0fe945f32b4357687989f3aee253cd7f" dependencies = [ "bstr", "gix-date", @@ -367,6 +412,23 @@ dependencies = [ "winnow", ] +[[package]] +name = "gix-attributes" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eefb48f42eac136a4a0023f49a54ec31be1c7a9589ed762c45dcb9b953f7ecc8" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror", + "unicode-bom", +] + [[package]] name = "gix-bitmap" version = "0.2.11" @@ -385,6 +447,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-command" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c22e086314095c43ffe5cdc5c0922d5439da4fd726f3b0438c56147c34dc225" +dependencies = [ + "bstr", + "gix-path", + "gix-trace", + "shell-words", +] + [[package]] name = "gix-commitgraph" version = "0.24.2" @@ -401,9 +475,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62129c75e4b6229fe15fb9838cdc00c655e87105b651e4edd7c183fc5288b5d1" +checksum = "53fafe42957e11d98e354a66b6bd70aeea00faf2f62dd11164188224a507c840" dependencies = [ "bstr", "gix-config-value", @@ -435,9 +509,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180b130a4a41870edfbd36ce4169c7090bca70e195da783dea088dd973daa59c" +checksum = "367ee9093b0c2b04fd04c5c7c8b6a1082713534eab537597ae343663a518fa99" dependencies = [ "bstr", "itoa", @@ -447,21 +521,29 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78e605593c2ef74980a534ade0909c7dc57cca72baa30cbb67d2dda621f99ac4" +checksum = "40b9bd8b2d07b6675a840b56a6c177d322d45fa082672b0dad8f063b25baf0a4" dependencies = [ "bstr", + "gix-command", + "gix-filter", + "gix-fs", "gix-hash", "gix-object", + "gix-path", + "gix-tempfile", + "gix-trace", + "gix-worktree", + "imara-diff", "thiserror", ] [[package]] name = "gix-discover" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bab49087ed3710caf77e473dc0efc54ca33d8ccc6441359725f121211482b1" +checksum = "fc27c699b63da66b50d50c00668bc0b7e90c3a382ef302865e891559935f3dbf" dependencies = [ "bstr", "dunce", @@ -475,9 +557,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.38.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4254037d20a247a0367aa79333750146a369719f0c6617fec4f5752cc62b37" +checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" dependencies = [ "crc32fast", "flate2", @@ -492,12 +574,34 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gix-filter" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00ce6ea5ac8fca7adbc63c48a1b9e0492c222c386aa15f513405f1003f2f4ab2" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline-blocking", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror", +] + [[package]] name = "gix-fs" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634b8a743b0aae03c1a74ee0ea24e8c5136895efac64ce52b3ea106e1c6f0613" +checksum = "c3338ff92a2164f5209f185ec0cd316f571a72676bb01d27e22f2867ba69f77a" dependencies = [ + "fastrand", "gix-features", "gix-utils", ] @@ -531,7 +635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242" dependencies = [ "gix-hash", - "hashbrown", + "hashbrown 0.14.3", "parking_lot", ] @@ -550,9 +654,9 @@ dependencies = [ [[package]] name = "gix-index" -version = "0.31.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549621f13d9ccf325a7de45506a3266af0d08f915181c5687abb5e8669bfd2e6" +checksum = "2d8c5a5f1c58edcbc5692b174cda2703aba82ed17d7176ff4c1752eb48b1b167" dependencies = [ "bitflags 2.5.0", "bstr", @@ -566,7 +670,8 @@ dependencies = [ "gix-object", "gix-traverse", "gix-utils", - "hashbrown", + "gix-validate", + "hashbrown 0.14.3", "itoa", "libc", "memmap2", @@ -577,9 +682,9 @@ dependencies = [ [[package]] name = "gix-lock" -version = "13.1.1" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c359f81f01b8352063319bcb39789b7ea0887b406406381106e38c4a34d049" +checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" dependencies = [ "gix-tempfile", "gix-utils", @@ -588,9 +693,9 @@ dependencies = [ [[package]] name = "gix-macros" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dff438f14e67e7713ab9332f5fd18c8f20eb7eb249494f6c2bf170522224032" +checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17" dependencies = [ "proc-macro2", "quote", @@ -599,9 +704,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4f8efae72030df1c4a81d02dbe2348e748d9b9a11e108ed6efbd846326e051" +checksum = "1fe2dc4a41191c680c942e6ebd630c8107005983c4679214fdb1007dcf5ae1df" dependencies = [ "bstr", "gix-actor", @@ -618,9 +723,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.59.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b55378c719693380f66d9dd21ce46721eed2981d8789fc698ec1ada6fa176e" +checksum = "e92b9790e2c919166865d0825b26cc440a387c175bed1b43a2fa99c0e9d45e98" dependencies = [ "arc-swap", "gix-date", @@ -638,9 +743,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.49.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6391aeaa030ad64aba346a9f5c69bb1c4e5c6fb4411705b03b40b49d8614ec30" +checksum = "7a8da51212dbff944713edb2141ed7e002eea326b8992070374ce13a6cb610b3" dependencies = [ "clru", "gix-chunk", @@ -656,6 +761,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-packetline-blocking" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31d42378a3d284732e4d589979930d0d253360eccf7ec7a80332e5ccb77e14a" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror", +] + [[package]] name = "gix-path" version = "0.10.7" @@ -669,6 +786,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-pathspec" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76cab098dc10ba2d89f634f66bf196dea4d7db4bf10b75c7a9c201c55a2ee19" +dependencies = [ + "bitflags 2.5.0", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror", +] + [[package]] name = "gix-quote" version = "0.4.12" @@ -682,9 +814,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.43.0" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4aba68b925101cb45d6df328979af0681364579db889098a0de75b36c77b65" +checksum = "3394a2997e5bc6b22ebc1e1a87b41eeefbcfcff3dbfa7c4bd73cb0ac8f1f3e2e" dependencies = [ "gix-actor", "gix-date", @@ -718,9 +850,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e34196e1969bd5d36e2fbc4467d893999132219d503e23474a8ad2b221cb1e8" +checksum = "63e08f8107ed1f93a83bcfbb4c38084c7cb3f6cd849793f1d5eec235f9b13b2b" dependencies = [ "bstr", "gix-date", @@ -734,9 +866,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7d393ae814eeaae41a333c0ff684b243121cc61ccdc5bbe9897094588047d" +checksum = "4181db9cfcd6d1d0fd258e91569dbb61f94cb788b441b5294dd7f1167a3e788f" dependencies = [ "gix-commitgraph", "gix-date", @@ -759,12 +891,28 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "gix-submodule" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921cd49924ac14b6611b22e5fb7bbba74d8780dc7ad26153304b64d1272460ac" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror", +] + [[package]] name = "gix-tempfile" -version = "13.1.1" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a761d76594f4443b675e85928e4902dec333273836bd386906f01e7e346a0d11" +checksum = "d3b0e276cd08eb2a22e9f286a4f13a222a01be2defafa8621367515375644b99" dependencies = [ + "dashmap", "gix-fs", "libc", "once_cell", @@ -774,16 +922,17 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b838b2db8f62c9447d483a4c28d251b67fee32741a82cb4d35e9eb4e9fdc5ab" +checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" [[package]] name = "gix-traverse" -version = "0.38.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95aef84bc777025403a09788b1e4815c06a19332e9e5d87a955e1ed7da9bf0cf" +checksum = "f20cb69b63eb3e4827939f42c05b7756e3488ef49c25c412a876691d568ee2a0" dependencies = [ + "bitflags 2.5.0", "gix-commitgraph", "gix-date", "gix-hash", @@ -796,9 +945,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0b24f3ecc79a5a53539de9c2e99425d0ef23feacdcf3faac983aa9a2f26849" +checksum = "0db829ebdca6180fbe32be7aed393591df6db4a72dbbc0b8369162390954d1cf" dependencies = [ "bstr", "gix-features", @@ -810,9 +959,9 @@ dependencies = [ [[package]] name = "gix-utils" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066432d4c277f9877f091279a597ea5331f68ca410efc874f0bdfb1cd348f92" +checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" dependencies = [ "fastrand", "unicode-normalization", @@ -820,9 +969,9 @@ dependencies = [ [[package]] name = "gix-validate" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e39fc6e06044985eac19dd34d474909e517307582e462b2eb4c8fa51b6241545" +checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" dependencies = [ "bstr", "thiserror", @@ -830,11 +979,12 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe78e03af9eec168eb187e05463a981c57f0a915f64b1788685a776bd2ef969c" +checksum = "53f6b7de83839274022aff92157d7505f23debf739d257984a300a35972ca94e" dependencies = [ "bstr", + "gix-attributes", "gix-features", "gix-fs", "gix-glob", @@ -843,6 +993,7 @@ dependencies = [ "gix-index", "gix-object", "gix-path", + "gix-validate", ] [[package]] @@ -858,6 +1009,12 @@ dependencies = [ "regex-syntax 0.8.2", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.3" @@ -884,11 +1041,13 @@ dependencies = [ name = "hawkeye-fmt" version = "5.5.1" dependencies = [ + "anyhow", "gix", "ignore", "regex", "serde", "snafu", + "time", "toml", "tracing", "walkdir", @@ -941,6 +1100,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "imara-diff" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8" +dependencies = [ + "ahash", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.2.5" @@ -948,7 +1117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", ] [[package]] @@ -957,6 +1126,15 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "static_assertions", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1313,6 +1491,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "smallvec" version = "1.13.2" @@ -1340,6 +1524,12 @@ dependencies = [ "syn", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.0" @@ -1608,6 +1798,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index c2a444f..75453a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 [workspace] members = [ diff --git a/Dockerfile b/Dockerfile index 44cc49b..5554413 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 FROM public.ecr.aws/docker/library/rust:1.76.0-alpine3.19 as build ENV RUSTFLAGS="-C target-feature=-crt-static" diff --git a/README.md b/README.md index fcae20e..05a9f6d 100644 --- a/README.md +++ b/README.md @@ -180,9 +180,18 @@ inceptionYear = 2023 # 'auto' means this feature tries to be enabled with: # * gix - if `basedir` is in a Git repository. # * ignore crate's gitignore rules - if `basedir` is not in a Git repository. -# 'enabled' means always enabled with gix; failed if it is impossible. +# 'enable' means always enabled with gix; failed if it is impossible. # default: 'auto' ignore = 'auto' +# If enabled, populate file attrs determinated by Git; possible value: ['auto', 'enable', 'disable'] +# Attributes contains: +# * 'hawkeye.git.fileCreatedYear' +# * 'hawkeye.git.fileModifiedYear' +# 'auto' means this feature tries to be enabled with: +# * gix - if `basedir` is in a Git repository. +# 'enable' means always enabled with gix; failed if it is impossible. +# default: 'disable' +attrs = 'disable' ``` ### Header style file diff --git a/action.yml b/action.yml index 14a00b4..8f289dd 100644 --- a/action.yml +++ b/action.yml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 name: HawkEye Action description: 'Check, format, or remove license headers.' diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6305590..c8f9b1a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 [package] name = "hawkeye" diff --git a/cli/build.rs b/cli/build.rs index f1bd573..f8c16e4 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::{borrow::Cow, sync::OnceLock}; diff --git a/cli/src/cli.rs b/cli/src/cli.rs index d6bca43..60cae41 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::path::{Path, PathBuf}; diff --git a/cli/src/main.rs b/cli/src/main.rs index 90b5644..4e1fd1f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use clap::{crate_description, FromArgMatches, Subcommand}; use tracing::level_filters::LevelFilter; diff --git a/fmt/Cargo.toml b/fmt/Cargo.toml index b2094fc..bf9b2ef 100644 --- a/fmt/Cargo.toml +++ b/fmt/Cargo.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 [package] name = "hawkeye-fmt" @@ -23,11 +26,16 @@ license.workspace = true repository.workspace = true [dependencies] -gix = { version = "0.61", default-features = false, features = ["excludes"] } +anyhow = "1.0" +gix = { version = "0.63", default-features = false, features = [ + "blob-diff", + "excludes", +] } ignore = "0.4.22" regex = "1.10.3" serde = { version = "1.0.197", features = ["derive"] } snafu.workspace = true +time = "0.3" toml.workspace = true tracing.workspace = true walkdir = "2.5.0" diff --git a/fmt/src/config/mod.rs b/fmt/src/config/mod.rs index 2e5460d..2062846 100644 --- a/fmt/src/config/mod.rs +++ b/fmt/src/config/mod.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::{ collections::{HashMap, HashSet}, @@ -54,16 +57,25 @@ pub struct Config { pub additional_headers: Vec, } -#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct Git { + pub attrs: FeatureGate, pub ignore: FeatureGate, } -#[derive(Debug, Clone, Copy, Default, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] +impl Default for Git { + fn default() -> Self { + Git { + attrs: FeatureGate::Disable, // expensive + ignore: FeatureGate::Auto, + } + } +} + +#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum FeatureGate { /// Determinate whether turn on the feature. - #[default] Auto, /// Force enable the feature. Enable, diff --git a/fmt/src/document/defaults.toml b/fmt/src/document/defaults.toml index ab96055..32ac41f 100644 --- a/fmt/src/document/defaults.toml +++ b/fmt/src/document/defaults.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 [ACTIONSCRIPT] pattern = "as" diff --git a/fmt/src/document/factory.rs b/fmt/src/document/factory.rs index a0f1173..81c92bc 100644 --- a/fmt/src/document/factory.rs +++ b/fmt/src/document/factory.rs @@ -11,13 +11,23 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::{ + borrow::Cow, collections::{HashMap, HashSet}, path::Path, }; -use crate::{config::Mapping, document::Document, header::model::HeaderDef}; +use snafu::ResultExt; +use time::{format_description, format_description::FormatItem}; + +use crate::{ + config::Mapping, document::Document, error::CreateDocumentSnafu, git::GitFileAttrs, + header::model::HeaderDef, Result, +}; pub struct DocumentFactory { mapping: HashSet, @@ -25,6 +35,8 @@ pub struct DocumentFactory { properties: HashMap, keywords: Vec, + git_file_attrs: HashMap, + year_formatter: Vec>, } impl DocumentFactory { @@ -33,16 +45,20 @@ impl DocumentFactory { definitions: HashMap, properties: HashMap, keywords: Vec, + git_file_attrs: HashMap, ) -> Self { + let year_formatter = format_description::parse("[year]").expect("cannot parse format"); Self { mapping, definitions, properties, keywords, + git_file_attrs, + year_formatter, } } - pub fn create_document(&self, filepath: &Path) -> std::io::Result { + pub fn create_document(&self, filepath: &Path) -> Result> { let lower_file_name = filepath .file_name() .map(|n| n.to_string_lossy().to_lowercase()) @@ -56,13 +72,39 @@ impl DocumentFactory { let header_def = self .definitions .get(&header_type) - .ok_or_else(|| std::io::Error::other(format!("header type {header_type} not found")))?; + .ok_or_else(|| std::io::Error::other(format!("header type {header_type} not found"))) + .context(CreateDocumentSnafu { + path: filepath.display().to_string(), + })?; + + let mut properties = self.properties.clone(); + + let filename = filepath + .file_name() + .map(|s| s.to_string_lossy()) + .unwrap_or_else(|| Cow::Borrowed("")) + .to_string(); + properties.insert("hawkeye.core.filename".to_string(), filename); + + if let Some(attrs) = self + .git_file_attrs + .get(filepath.to_str().expect("path is never empty")) + { + properties.insert( + "hawkeye.git.fileCreatedYear".to_string(), + attrs.created_time.format(self.year_formatter.as_slice()), + ); + properties.insert( + "hawkeye.git.fileModifiedYear".to_string(), + attrs.modified_time.format(self.year_formatter.as_slice()), + ); + } Document::new( filepath.to_path_buf(), header_def.clone(), &self.keywords, - self.properties.clone(), + properties, ) } } diff --git a/fmt/src/document/mod.rs b/fmt/src/document/mod.rs index bffbf13..38270f9 100644 --- a/fmt/src/document/mod.rs +++ b/fmt/src/document/mod.rs @@ -11,15 +11,22 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 -use std::{borrow::Cow, collections::HashMap, fs, fs::File, io::BufRead, path::PathBuf}; +use std::{collections::HashMap, fs, fs::File, io::BufRead, path::PathBuf}; use snafu::ResultExt; +use tracing::debug; use crate::{ - error::SaveDocumentSnafu, - header, - header::{matcher::HeaderMatcher, model::HeaderDef, parser::HeaderParser}, + error::{CreateDocumentSnafu, SaveDocumentSnafu}, + header::{ + matcher::HeaderMatcher, + model::HeaderDef, + parser::{parse_header, FileContent, HeaderParser}, + }, Result, }; @@ -41,14 +48,25 @@ impl Document { header_def: HeaderDef, keywords: &[String], properties: HashMap, - ) -> std::io::Result { - let parser = header::parser::parse_header(&filepath, &header_def, keywords)?; - Ok(Self { - filepath, - header_def, - properties, - parser, - }) + ) -> Result> { + match FileContent::new(&filepath) { + Ok(content) => Ok(Some(Self { + parser: parse_header(content, &header_def, keywords), + filepath, + header_def, + properties, + })), + Err(e) => { + if matches!(e.kind(), std::io::ErrorKind::InvalidData) { + debug!("skip non-textual file: {}", filepath.display()); + Ok(None) + } else { + Err(e).context(CreateDocumentSnafu { + path: filepath.display().to_string(), + }) + } + } + } } pub fn is_unsupported(&self) -> bool { @@ -132,15 +150,7 @@ impl Document { } pub(crate) fn merge_properties(&self, s: &str) -> String { - let mut properties = self.properties.clone(); - let filename = self - .filepath - .file_name() - .map(|s| s.to_string_lossy()) - .unwrap_or_else(|| Cow::Borrowed("")) - .to_string(); - properties.insert("hawkeye.core.filename".to_string(), filename); - merge_properties(&properties, s) + merge_properties(&self.properties, s) } } diff --git a/fmt/src/document/model.rs b/fmt/src/document/model.rs index d915743..9721d8e 100644 --- a/fmt/src/document/model.rs +++ b/fmt/src/document/model.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::collections::HashMap; diff --git a/fmt/src/error.rs b/fmt/src/error.rs index 3f5da05..44c773b 100644 --- a/fmt/src/error.rs +++ b/fmt/src/error.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use snafu::Snafu; @@ -139,4 +142,12 @@ pub enum Error { #[snafu(implicit)] loc: snafu::Location, }, + + #[snafu(display("cannot obtain file attrs from Git: {}", source))] + GitFileAttrs { + #[snafu(source)] + source: anyhow::Error, + #[snafu(implicit)] + loc: snafu::Location, + }, } diff --git a/fmt/src/git.rs b/fmt/src/git.rs index cc4ca8b..fdac915 100644 --- a/fmt/src/git.rs +++ b/fmt/src/git.rs @@ -11,8 +11,15 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 -use std::path::Path; +use std::{ + collections::{hash_map::Entry, HashMap}, + convert::Infallible, + path::Path, +}; use gix::Repository; use snafu::IntoError; @@ -20,39 +27,134 @@ use tracing::info; use crate::{ config, + config::FeatureGate, error::{GixDiscoverOpSnafu, InvalidConfigSnafu}, Result, }; -pub fn discover(basedir: &Path, config: config::Git) -> Result> { - if config.ignore.is_disable() { - return Ok(None); +#[derive(Debug, Clone)] +pub struct GitContext { + pub repo: Option, + pub config: config::Git, +} + +pub fn discover(basedir: &Path, config: config::Git) -> Result { + let feature = resolve_features(&config); + + if feature.is_disable() { + return Ok(GitContext { repo: None, config }); } - let is_auto = config.ignore.is_auto(); match gix::discover(basedir) { Ok(repo) => match repo.worktree() { None => { let message = "bare repository detected"; - if is_auto { - info!("git.ignore=auto is resolved to fallback; {message}"); - Ok(None) + if feature.is_auto() { + info!(?config, "git config is resolved to disabled; {message}"); + Ok(GitContext { repo: None, config }) } else { InvalidConfigSnafu { message }.fail() } } Some(_) => { - info!("git.ignore=auto is resolved to enabled"); - Ok(Some(repo)) + info!("git config is resolved to enabled"); + Ok(GitContext { + repo: Some(repo), + config, + }) } }, Err(err) => { - if is_auto { - info!(?err, "git.ignore=auto is resolved to disabled"); - Ok(None) + if feature.is_auto() { + info!(?err, ?config, "git config is resolved to disabled"); + Ok(GitContext { repo: None, config }) } else { Err(GixDiscoverOpSnafu {}.into_error(Box::new(err))) } } } } + +fn resolve_features(config: &config::Git) -> FeatureGate { + let features = [config.attrs, config.ignore]; + for feature in features.iter() { + if feature.is_enable() { + return FeatureGate::Enable; + } + } + for feature in features.iter() { + if feature.is_auto() { + return FeatureGate::Auto; + } + } + FeatureGate::Disable +} + +#[derive(Debug)] +pub struct GitFileAttrs { + pub created_time: gix::date::Time, + pub modified_time: gix::date::Time, +} + +pub fn resolve_file_attrs( + git_context: GitContext, +) -> anyhow::Result> { + let mut attrs = HashMap::new(); + + if git_context.config.attrs.is_disable() { + return Ok(attrs); + } + + let repo = match git_context.repo { + Some(repo) => repo, + None => return Ok(attrs), + }; + + let workdir = repo.work_dir().expect("workdir cannot be absent"); + let workdir = workdir.canonicalize()?; + + let mode = gix::diff::blob::pipeline::Mode::ToGit; + let mut cache = repo.diff_resource_cache(mode, Default::default())?; + + let head = repo.head_commit()?; + let mut prev_commit = head.clone(); + + for info in head.ancestors().all()? { + let info = info?; + let this_commit = info.object()?; + let time = this_commit.time()?; + + let tree = this_commit.tree()?; + let mut changes = tree.changes()?; + changes.track_path().for_each_to_obtain_tree_with_cache( + &prev_commit.tree()?, + &mut cache, + |change| { + let filepath = workdir.join(change.location.to_string()); + let filepath = filepath.display().to_string(); + + match attrs.entry(filepath) { + Entry::Occupied(mut ent) => { + let attrs: &GitFileAttrs = ent.get(); + ent.insert(GitFileAttrs { + created_time: time.min(attrs.created_time), + modified_time: time.max(attrs.modified_time), + }); + } + Entry::Vacant(ent) => { + ent.insert(GitFileAttrs { + created_time: time, + modified_time: time, + }); + } + } + + Ok::<_, Infallible>(Default::default()) + }, + )?; + prev_commit = this_commit; + cache.clear_resource_cache(); + } + + Ok(attrs) +} diff --git a/fmt/src/header/defaults.toml b/fmt/src/header/defaults.toml index 881e2bd..9ed4707 100644 --- a/fmt/src/header/defaults.toml +++ b/fmt/src/header/defaults.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 [SHARPSTAR_STYLE] firstLine = '#*' diff --git a/fmt/src/header/matcher.rs b/fmt/src/header/matcher.rs index 476e9ae..cae8085 100644 --- a/fmt/src/header/matcher.rs +++ b/fmt/src/header/matcher.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::fmt::{Display, Formatter}; diff --git a/fmt/src/header/mod.rs b/fmt/src/header/mod.rs index 24031a8..3f03b12 100644 --- a/fmt/src/header/mod.rs +++ b/fmt/src/header/mod.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 pub mod matcher; pub mod model; diff --git a/fmt/src/header/model.rs b/fmt/src/header/model.rs index b287385..8b00369 100644 --- a/fmt/src/header/model.rs +++ b/fmt/src/header/model.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::collections::HashMap; diff --git a/fmt/src/header/parser.rs b/fmt/src/header/parser.rs index f5eaf44..a81fb2b 100644 --- a/fmt/src/header/parser.rs +++ b/fmt/src/header/parser.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::{ fmt::{Display, Formatter}, @@ -30,11 +33,10 @@ pub struct HeaderParser { } pub fn parse_header( - file: &Path, + mut file_content: FileContent, header_def: &HeaderDef, keywords: &[String], -) -> std::io::Result { - let mut file_content = FileContent::new(file)?; +) -> HeaderParser { let mut line = file_content.next_line(); // 1. find begin position @@ -64,11 +66,11 @@ pub fn parse_header( None }; - Ok(HeaderParser { + HeaderParser { begin_pos, end_pos, file_content, - }) + } } fn find_first_position( diff --git a/fmt/src/lib.rs b/fmt/src/lib.rs index 37affca..3d1aa5d 100644 --- a/fmt/src/lib.rs +++ b/fmt/src/lib.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use crate::error::Error; diff --git a/fmt/src/license/mod.rs b/fmt/src/license/mod.rs index 58e93ce..0f3a780 100644 --- a/fmt/src/license/mod.rs +++ b/fmt/src/license/mod.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use snafu::OptionExt; diff --git a/fmt/src/processor.rs b/fmt/src/processor.rs index f29e270..4085d32 100644 --- a/fmt/src/processor.rs +++ b/fmt/src/processor.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::{ fs, @@ -18,15 +21,15 @@ use std::{ }; use snafu::{ensure, ResultExt}; -use tracing::debug; use crate::{ config::Config, document::{factory::DocumentFactory, model::default_mapping, Document}, error::{ - CreateDocumentSnafu, DeserializeSnafu, InvalidConfigSnafu, LoadConfigSnafu, + DeserializeSnafu, GitFileAttrsSnafu, InvalidConfigSnafu, LoadConfigSnafu, TryMatchHeaderSnafu, }, + git, header::{ matcher::HeaderMatcher, model::{default_headers, deserialize_header_definitions}, @@ -70,10 +73,7 @@ pub fn check_license_header(run_config: PathBuf, callback: &mut C) } ); - let header_matcher = { - let header_source = HeaderSource::from_config(&config)?; - HeaderMatcher::new(header_source.content) - }; + let git_context = git::discover(&basedir, config.git)?; let selected_files = { let selection = Selection::new( @@ -82,13 +82,13 @@ pub fn check_license_header(run_config: PathBuf, callback: &mut C) &config.includes, &config.excludes, config.use_default_excludes, - config.git, + git_context.clone(), ); selection.select()? }; let mapping = { - let mut mapping = config.mapping; + let mut mapping = config.mapping.clone(); if config.use_default_mapping { let default_mapping = default_mapping(); mapping.extend(default_mapping); @@ -109,23 +109,27 @@ pub fn check_license_header(run_config: PathBuf, callback: &mut C) defs }; - let document_factory = - DocumentFactory::new(mapping, definitions, config.properties, config.keywords); + let header_matcher = { + let header_source = HeaderSource::from_config(&config)?; + HeaderMatcher::new(header_source.content) + }; + + let git_file_attrs = git::resolve_file_attrs(git_context).context(GitFileAttrsSnafu)?; + + let document_factory = DocumentFactory::new( + mapping, + definitions, + config.properties, + config.keywords, + git_file_attrs, + ); for file in selected_files { - let document = document_factory.create_document(&file); - let document = match document { - Ok(document) => document, - Err(e) => { - if matches!(e.kind(), std::io::ErrorKind::InvalidData) { - debug!("skip non-textual file: {}", file.display()); - callback.on_unknown(&file)?; - continue; - } else { - return Err(e).context(CreateDocumentSnafu { - path: file.display().to_string(), - }); - } + let document = match document_factory.create_document(&file)? { + Some(document) => document, + None => { + callback.on_unknown(&file)?; + continue; } }; diff --git a/fmt/src/selection.rs b/fmt/src/selection.rs index 579bac2..ba2fefd 100644 --- a/fmt/src/selection.rs +++ b/fmt/src/selection.rs @@ -11,6 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::path::{Path, PathBuf}; @@ -20,20 +23,19 @@ use tracing::debug; use walkdir::WalkDir; use crate::{ - config, error::{ GixCheckExcludeOpSnafu, GixExcludeOpSnafu, ResolveAbsolutePathSnafu, SelectFilesSnafu, SelectWithIgnoreSnafu, TraverseDirSnafu, }, - git, Result, + git::GitContext, + Result, }; pub struct Selection { basedir: PathBuf, includes: Vec, excludes: Vec, - - git: config::Git, + git_context: GitContext, } impl Selection { @@ -43,7 +45,7 @@ impl Selection { includes: &[String], excludes: &[String], use_default_excludes: bool, - git: config::Git, + git_context: GitContext, ) -> Selection { let includes = if includes.is_empty() { INCLUDES.iter().map(|s| s.to_string()).collect() @@ -65,7 +67,7 @@ impl Selection { basedir, includes, excludes, - git, + git_context, } } @@ -101,13 +103,14 @@ impl Selection { }, ); - let result = match git::discover(&self.basedir, self.git)? { + let ignore = self.git_context.config.ignore.is_auto(); + let result = match self.git_context.repo { None => select_files_with_ignore( &self.basedir, &includes, &excludes, &reverse_excludes, - self.git.ignore.is_auto(), + ignore, )?, Some(repo) => { select_files_with_git(&self.basedir, &includes, &excludes, &reverse_excludes, repo)? @@ -219,8 +222,13 @@ fn select_files_with_git( let rela_path = path .strip_prefix(&workdir) .expect("git repository encloses iteration"); + let mode = Some(if file_type.is_dir() { + gix::index::entry::Mode::DIR + } else { + gix::index::entry::Mode::FILE + }); let platform = excludes - .at_path(rela_path, Some(file_type.is_dir())) + .at_path(rela_path, mode) .context(GixCheckExcludeOpSnafu)?; if file_type.is_dir() { diff --git a/fmt/tests/tests.rs b/fmt/tests/tests.rs index bf6d5ec..8f1b684 100644 --- a/fmt/tests/tests.rs +++ b/fmt/tests/tests.rs @@ -11,10 +11,16 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +// +// Copyright 2024 - 2024, tison and the HawkEye contributors +// SPDX-License-Identifier: Apache-2.0 use std::path::Path; -use hawkeye_fmt::header::{model::default_headers, parser::parse_header}; +use hawkeye_fmt::header::{ + model::default_headers, + parser::{parse_header, FileContent}, +}; #[test] fn test_remove_file_only_header() { @@ -23,7 +29,8 @@ fn test_remove_file_only_header() { let def = defs.get("script_style").unwrap().clone(); let keywords = vec!["copyright".to_string()]; - let document = parse_header(file, &def, &keywords).unwrap(); + let file_content = FileContent::new(file).unwrap(); + let document = parse_header(file_content, &def, &keywords); let end_pos = document.end_pos.unwrap(); let content = document.file_content.content(); assert!(content[end_pos..].trim().is_empty()); @@ -36,7 +43,8 @@ fn test_two_headers_should_only_remove_the_first() { let def = defs.get("doubleslash_style").unwrap().clone(); let keywords = vec!["copyright".to_string()]; - let document = parse_header(file, &def, &keywords).unwrap(); + let file_content = FileContent::new(file).unwrap(); + let document = parse_header(file_content, &def, &keywords); let end_pos = document.end_pos.unwrap(); let content = document.file_content.content(); assert!(content[end_pos..].contains("Copyright 2015 The Prometheus Authors")); diff --git a/licenserc.toml b/licenserc.toml index 003f7fe..2faff9e 100644 --- a/licenserc.toml +++ b/licenserc.toml @@ -11,8 +11,28 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 + +inlineHeader = """ +Copyright ${inceptionYear} ${copyrightOwner} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -headerPath = "Apache-2.0.txt" + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Copyright ${hawkeye.git.fileCreatedYear} - ${hawkeye.git.fileModifiedYear}, ${copyrightOwner} and the ${projectName} contributors +SPDX-License-Identifier: Apache-2.0 +""" excludes = [ # Plain text files AS IS @@ -26,6 +46,11 @@ excludes = [ ".github/workflows/release.yml", ] +[git] +attrs = 'auto' +ignore = 'auto' + [properties] inceptionYear = 2024 copyrightOwner = "tison " +projectName = "HawkEye" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 093997d..3f4c18f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 [toolchain] channel = "stable" diff --git a/rustfmt.toml b/rustfmt.toml index 8a8512b..8328a59 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 imports_granularity = "Crate" group_imports = "StdExternalCrate" diff --git a/tests/it.py b/tests/it.py index 9dd6a63..b221547 100755 --- a/tests/it.py +++ b/tests/it.py @@ -12,6 +12,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Copyright 2024 - 2024, tison and the HawkEye contributors +# SPDX-License-Identifier: Apache-2.0 from pathlib import Path import subprocess