repro-env-0.4.1/.cargo/audit.toml000064400000000000000000000000541046102023000147160ustar 00000000000000[advisories] ignore = ["RUSTSEC-2023-0071"] repro-env-0.4.1/.cargo_vcs_info.json0000644000000001360000000000100127530ustar { "git": { "sha1": "b78b6e915594d1ac9027a7932d6848b07b6a8680" }, "path_in_vcs": "" }repro-env-0.4.1/.github/FUNDING.yml000064400000000000000000000000211046102023000147110ustar 00000000000000github: [kpcyrd] repro-env-0.4.1/.github/workflows/nix.yml000064400000000000000000000010441046102023000164600ustar 00000000000000name: NixOS/nix on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '0 9 * * 1' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v20 with: nix_path: nixpkgs=channel:nixos-unstable - uses: DeterminateSystems/magic-nix-cache-action@v2 - run: nix build . - run: ls -lah result/bin/ - run: sha256sum result/bin/repro-env - run: result/bin/repro-env --help - run: ldd result/bin/repro-env repro-env-0.4.1/.github/workflows/rust.yml000064400000000000000000000123131046102023000166600ustar 00000000000000name: Rust on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '0 9 * * 1' env: CARGO_TERM_COLOR: always jobs: build-bootstrapped: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: 🏗️ Setup build cache uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ ~/.cache/repro-env/pkgs/ target/ key: ${{ runner.os }}-cargo-release-bootstrapped-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo-release-bootstrapped- - name: 🛠️ Build run: make - name: 🔍 SHA256 run: sha256sum target/x86_64-unknown-linux-musl/release/repro-env - name: 📦 Upload binary uses: actions/upload-artifact@v4 with: name: bin path: target/x86_64-unknown-linux-musl/release/repro-env build-macos: runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Set up cargo cache uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo-release- - name: Build run: cargo build --release --verbose build-ubuntu: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Install dependencies run: sudo apt-get install -y repro-env - name: 🏗️ Setup build cache uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ ~/.cache/repro-env/pkgs/ target/ key: ${{ runner.os }}-cargo-release-ubuntu-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo-release-ubuntu- - name: 🛠️ Build run: repro-env build -- make build2 - name: 🔍 SHA256 run: sha256sum target/x86_64-unknown-linux-musl/release/repro-env integration-test: needs: build-bootstrapped strategy: fail-fast: false matrix: test: - folder: examples/rust cmd: cargo build --release output: target/release/rust sha256: ea8997342bef06680784c9ea6ef7e22f7ba089e98927e870905503d0bf39acb1 - folder: examples/golang cmd: go build . output: hello sha256: dd41bdb93af4fb798f6b079368d6ee50a6907044c292ba6d2c98420ca8f012bf - folder: examples/debian cmd: gcc -static -o hello hello.c output: hello sha256: 2b24cfa838189c3aa6fa2440afe8508654830b6d3bad85d9b31958ae5c3fb429 - folder: examples/archlinux cmd: gcc -static -o hello hello.c output: hello sha256: 04e03fd681793db603feb66a4c8e8df3858f1e31372dc017d7a0cf255faf84c5 #- folder: examples/alpine # cmd: gcc -static -o hello hello.c # output: hello # sha256: a312585d54252990c2264351769f1cf2539f674db1142c7ea18fe10096debf12 name: ${{ matrix.test.folder }} runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 - name: 🏗️ Setup build cache uses: actions/cache@v4 with: path: | ~/.cache/repro-env/pkgs/ key: repro-env-${{ matrix.test.folder }} - name: 🛠️ Build artifact run: chmod +x bin/repro-env && bin/repro-env -C ${{ matrix.test.folder }} build -- ${{ matrix.test.cmd }} - name: 🔍 Verify artifact run: cd ${{ matrix.test.folder }} && echo "${{ matrix.test.sha256 }} ${{ matrix.test.output }}" | sha256sum -c - unit-test: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Set up cargo cache uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo-debug- - name: Run clippy run: cargo clippy -- -D warnings - name: Run tests run: cargo test --verbose deny: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Run cargo deny run: | docker run --rm -v "$PWD:/src" -w /src alpine:edge sh -c ' set -e apk add cargo-deny --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing/ || apk add cargo-deny --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community/ || apk add cargo-deny apk add cargo exec cargo deny check ' fmt: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Run cargo fmt run: cargo fmt --all -- --check docs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Install dependencies run: sudo apt-get install scdoc - name: Build run: make docs repro-env-0.4.1/.gitignore000064400000000000000000000000321046102023000135260ustar 00000000000000/target /docs/*.1 /result repro-env-0.4.1/Cargo.lock0000644000002464610000000000100107430ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array 0.14.7", ] [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", "zeroize", ] [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", "cipher", "ctr", "ghash", "subtle", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstream" version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "ar" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" [[package]] name = "ascii-canvas" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array 0.14.7", ] [[package]] name = "block-padding" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array 0.14.7", ] [[package]] name = "blowfish" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", "cipher", ] [[package]] name = "buffered-reader" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd098763fdb64579407a8c83cf0d751e6d4a7e161d0114c89cc181a2ca760ec8" dependencies = [ "lazy_static", "libc", ] [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "camellia" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" dependencies = [ "byteorder", "cipher", ] [[package]] name = "cast5" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" dependencies = [ "cipher", ] [[package]] name = "cc" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" [[package]] name = "cfb-mode" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" dependencies = [ "cipher", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets 0.52.6", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", "zeroize", ] [[package]] name = "clap" version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_complete" version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6ae69fbb0833c6fcd5a8d4b8609f108c7ad95fc11e248d853ff2c42a90df26a" dependencies = [ "clap", ] [[package]] name = "clap_derive" version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clone-file" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3998407c42ae2d750d4dfb7478d3b4995265913b2b13ea03f73bc8fc6478e9b2" dependencies = [ "nix 0.26.4", ] [[package]] name = "cmac" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" dependencies = [ "cipher", "dbl", "digest", ] [[package]] name = "colorchoice" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc" version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array 0.14.7", "rand_core", "subtle", "zeroize", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", "rand_core", "typenum", ] [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] [[package]] name = "curve25519-dalek" version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", "rustc_version", "subtle", "zeroize", ] [[package]] name = "curve25519-dalek-derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "dbl" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" dependencies = [ "generic-array 0.14.7", ] [[package]] name = "der" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", "zeroize", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "des" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" dependencies = [ "cipher", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", "crypto-common", "subtle", ] [[package]] name = "dirs" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-next" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ "cfg-if", "dirs-sys-next", ] [[package]] name = "dirs-sys" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", "redox_users", "windows-sys 0.48.0", ] [[package]] name = "dirs-sys-next" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", "winapi", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "dsa" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" dependencies = [ "digest", "num-bigint-dig", "num-traits", "pkcs8", "rfc6979", "sha2", "signature", "zeroize", ] [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "eax" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9954fabd903b82b9d7a68f65f97dc96dd9ad368e40ccc907a7c19d53e6bfac28" dependencies = [ "aead", "cipher", "cmac", "ctr", "subtle", ] [[package]] name = "ecb" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" dependencies = [ "cipher", ] [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest", "elliptic-curve", "rfc6979", "signature", "spki", ] [[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "signature", ] [[package]] name = "ed25519-dalek" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", "rand_core", "serde", "sha2", "subtle", "zeroize", ] [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", "digest", "ff", "generic-array 0.14.7", "group", "hkdf", "pem-rfc7468", "pkcs8", "rand_core", "sec1", "subtle", "zeroize", ] [[package]] name = "ena" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] [[package]] name = "env_filter" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ "anstream", "anstyle", "env_filter", "humantime", "log", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fd-lock" version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", "rustix", "windows-sys 0.52.0", ] [[package]] name = "ff" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core", "subtle", ] [[package]] name = "fiat-crypto" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall 0.4.1", "windows-sys 0.52.0", ] [[package]] name = "fixedbitset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", "zeroize", ] [[package]] name = "generic-array" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" dependencies = [ "typenum", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", "libc", "wasi", "wasm-bindgen", ] [[package]] name = "ghash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", ] [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core", "subtle", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest", ] [[package]] name = "http" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", "http", "http-body", "httparse", "itoa", "pin-project-lite", "smallvec", "tokio", "want", ] [[package]] name = "hyper-rustls" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", "http", "hyper", "hyper-util", "rustls", "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] [[package]] name = "hyper-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ "bytes", "futures-channel", "futures-util", "http", "http-body", "hyper", "pin-project-lite", "socket2", "tokio", "tower", "tower-service", "tracing", ] [[package]] name = "iana-time-zone" version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "idea" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" dependencies = [ "cipher", ] [[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "idna" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd69211b9b519e98303c015e21a007e293db403b6c85b9b124e133d25e242cdd" dependencies = [ "icu_normalizer", "icu_properties", "smallvec", "utf8_iter", ] [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", "serde", ] [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "block-padding", "generic-array 0.14.7", ] [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lalrpop" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", "ena", "itertools", "lalrpop-util", "petgraph", "regex", "regex-syntax", "string_cache", "term", "tiny-keccak", "unicode-xid", "walkdir", ] [[package]] name = "lalrpop-util" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ "regex-automata", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ "spin", ] [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", ] [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lz4_flex" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" dependencies = [ "twox-hash", ] [[package]] name = "lzma-rs" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" dependencies = [ "byteorder", "crc", ] [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", "digest", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memsec" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c797b9d6bb23aab2fc369c65f871be49214f5c759af65bde26ffaaa2b646b492" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", ] [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", ] [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "num-bigint-dig" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ "byteorder", "lazy_static", "libm", "num-integer", "num-iter", "num-traits", "rand", "smallvec", "zeroize", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-iter" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "object" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "p256" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p384" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p521" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" dependencies = [ "base16ct", "ecdsa", "elliptic-curve", "primeorder", "rand_core", "sha2", ] [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall 0.5.3", "smallvec", "windows-targets 0.52.6", ] [[package]] name = "peekread" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978122cdc72f39ed3c6343907453570570699169aace43dd09d46b52cc5f681d" [[package]] name = "pem-rfc7468" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", ] [[package]] name = "phf_shared" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ "siphasher", ] [[package]] name = "pin-project" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs1" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ "der", "pkcs8", "spki", ] [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", ] [[package]] name = "polyval" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", "opaque-debug", "universal-hash", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2288c0e17cc8d342c712bb43a257a80ebffce59cdb33d5000d8348f3ec02528b" dependencies = [ "zerocopy", "zerocopy-derive", ] [[package]] name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "primeorder" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quinn" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", "rustls", "thiserror", "tokio", "tracing", ] [[package]] name = "quinn-proto" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" dependencies = [ "bytes", "rand", "ring", "rustc-hash", "rustls", "slab", "thiserror", "tinyvec", "tracing", ] [[package]] name = "quinn-udp" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" dependencies = [ "libc", "once_cell", "socket2", "windows-sys 0.52.0", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "redox_users" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", "thiserror", ] [[package]] name = "regex" version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "repro-env" version = "0.4.1" dependencies = [ "anyhow", "ar", "bytes", "clap", "clap_complete", "clone-file", "data-encoding", "dirs", "env_logger", "fd-lock", "flate2", "hex", "indexmap", "log", "lz4_flex", "lzma-rs", "memchr", "nix 0.29.0", "peekread", "reqwest", "ruzstd", "sequoia-openpgp", "serde", "serde_json", "sha1", "sha2", "tar", "tempfile", "time", "tokio", "toml", "urlencoding", ] [[package]] name = "reqwest" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64", "bytes", "futures-core", "futures-util", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", "hyper-util", "ipnet", "js-sys", "log", "mime", "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", "rustls-native-certs", "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", "tokio-rustls", "tokio-socks", "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", "winreg", ] [[package]] name = "rfc6979" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac", "subtle", ] [[package]] name = "ring" version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", "getrandom", "libc", "spin", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "ripemd" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ "digest", ] [[package]] name = "rsa" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ "const-oid", "digest", "num-bigint-dig", "num-integer", "num-traits", "pkcs1", "pkcs8", "rand_core", "signature", "spki", "subtle", "zeroize", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustls" version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" dependencies = [ "openssl-probe", "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ruzstd" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5022b253619b1ba797f243056276bed8ed1a73b0f5a7ce7225d524067644bf8f" dependencies = [ "byteorder", "twox-hash", ] [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schannel" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", "generic-array 0.14.7", "pkcs8", "subtle", "zeroize", ] [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "sequoia-openpgp" version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13261ee216b44d932ef93b2d4a75d45199bef77864bcc5b77ecfc7bc0ecb02d6" dependencies = [ "aes", "aes-gcm", "anyhow", "base64", "block-padding", "blowfish", "buffered-reader", "camellia", "cast5", "cfb-mode", "chrono", "cipher", "des", "digest", "dsa", "dyn-clone", "eax", "ecb", "ecdsa", "ed25519", "ed25519-dalek", "getrandom", "idea", "idna 1.0.2", "lalrpop", "lalrpop-util", "lazy_static", "libc", "md-5", "memsec", "num-bigint-dig", "once_cell", "p256", "p384", "p521", "rand", "rand_core", "regex", "regex-syntax", "ripemd", "rsa", "sha1collisiondetection", "sha2", "thiserror", "twofish", "typenum", "x25519-dalek", "xxhash-rust", ] [[package]] name = "serde" version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha1collisiondetection" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b" dependencies = [ "const-oid", "digest", "generic-array 1.1.0", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core", ] [[package]] name = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", "phf_shared", "precomputed-hash", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tar" version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", "xattr", ] [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "term" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ "dirs-next", "rustversion", "winapi", ] [[package]] name = "thiserror" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ "crunchy", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tinyvec" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-rustls" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ "rustls", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-socks" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", "thiserror", "tokio", ] [[package]] name = "tokio-util" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "toml" version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a44eede9b727419af8095cb2d72fab15487a541f54647ad4414b34096ee4631" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1490595c74d930da779e944f5ba2ecdf538af67df1a9848cbd156af43c1b7cf0" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "tower" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", "pin-project", "pin-project-lite", "tokio", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "twofish" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" dependencies = [ "cipher", ] [[package]] name = "twox-hash" version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "static_assertions", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-xid" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle", ] [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", "percent-encoding", ] [[package]] name = "urlencoding" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-streams" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", ] [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" dependencies = [ "memchr", ] [[package]] name = "winreg" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "x25519-dalek" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core", "zeroize", ] [[package]] name = "xattr" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", "linux-raw-sys", "rustix", ] [[package]] name = "xxhash-rust" version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" [[package]] name = "yoke" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerofrom" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", "syn", ] repro-env-0.4.1/Cargo.toml0000644000000052570000000000100107620ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "repro-env" version = "0.4.1" authors = ["kpcyrd "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Dependency lockfiles for reproducible build environments 📦🔒" readme = "README.md" categories = ["command-line-utilities"] license = "MIT OR Apache-2.0" repository = "https://github.com/kpcyrd/repro-env" [lib] name = "repro_env" path = "src/lib.rs" [[bin]] name = "repro-env" path = "src/main.rs" [dependencies.anyhow] version = "1.0.71" [dependencies.ar] version = "0.9.0" [dependencies.bytes] version = "1.4.0" [dependencies.clap] version = "4" features = ["derive"] [dependencies.clap_complete] version = "4" [dependencies.clone-file] version = "0.1.0" [dependencies.data-encoding] version = "2.4.0" [dependencies.dirs] version = "5.0.1" [dependencies.env_logger] version = "0.11" [dependencies.fd-lock] version = "4.0.0" [dependencies.flate2] version = "1.0.26" [dependencies.hex] version = "0.4.3" [dependencies.indexmap] version = "2.1.0" features = ["serde"] [dependencies.log] version = "0.4.19" [dependencies.lz4_flex] version = "0.11.1" [dependencies.lzma-rs] version = "0.3.0" [dependencies.memchr] version = "2.5.0" [dependencies.nix] version = "0.29" features = ["sched"] default-features = false [dependencies.peekread] version = "0.1.1" [dependencies.reqwest] version = "0.12" features = [ "rustls-tls-native-roots", "socks", "stream", ] default-features = false [dependencies.ruzstd] version = "0.7" [dependencies.sequoia-openpgp] version = "1.18" features = [ "crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto", ] default-features = false [dependencies.serde] version = "1" features = ["derive"] [dependencies.serde_json] version = "1" [dependencies.sha1] version = "0.10.5" [dependencies.sha2] version = "0.10.7" [dependencies.tar] version = "0.4.38" [dependencies.tempfile] version = "3.6.0" [dependencies.time] version = "0.3" features = ["formatting"] [dependencies.tokio] version = "1" features = [ "macros", "rt-multi-thread", "fs", "process", "signal", ] [dependencies.toml] version = "0.8" [dependencies.urlencoding] version = "2.1.2" repro-env-0.4.1/Cargo.toml.orig0000644000000030410000000000100117060ustar [package] name = "repro-env" version = "0.4.1" description = "Dependency lockfiles for reproducible build environments 📦🔒" authors = ["kpcyrd "] license = "MIT OR Apache-2.0" repository = "https://github.com/kpcyrd/repro-env" categories = ["command-line-utilities"] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.71" ar = "0.9.0" bytes = "1.4.0" clap = { version = "4", features = ["derive"] } clap_complete = "4" clone-file = "0.1.0" data-encoding = "2.4.0" dirs = "5.0.1" env_logger = "0.11" fd-lock = "4.0.0" flate2 = "1.0.26" hex = "0.4.3" indexmap = { version = "2.1.0", features = ["serde"] } log = "0.4.19" lz4_flex = "0.11.1" lzma-rs = "0.3.0" memchr = "2.5.0" nix = { version = "0.29", default-features = false, features = ["sched"] } peekread = "0.1.1" reqwest = { version = "0.12", features = ["rustls-tls-native-roots", "socks", "stream"], default-features = false } ruzstd = "0.7" # only the cert parser is used, but sequoia-openpgp doesn't allow building with no backend sequoia-openpgp = { version = "1.18", default-features = false, features = ["crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto"] } serde = { version = "1", features = ["derive"] } serde_json = "1" sha1 = "0.10.5" sha2 = "0.10.7" tar = "0.4.38" tempfile = "3.6.0" time = { version = "0.3", features = ["formatting"] } tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs", "process", "signal"] } toml = "0.8" urlencoding = "2.1.2" repro-env-0.4.1/Cargo.toml.orig000064400000000000000000000030411046102023000144300ustar 00000000000000[package] name = "repro-env" version = "0.4.1" description = "Dependency lockfiles for reproducible build environments 📦🔒" authors = ["kpcyrd "] license = "MIT OR Apache-2.0" repository = "https://github.com/kpcyrd/repro-env" categories = ["command-line-utilities"] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.71" ar = "0.9.0" bytes = "1.4.0" clap = { version = "4", features = ["derive"] } clap_complete = "4" clone-file = "0.1.0" data-encoding = "2.4.0" dirs = "5.0.1" env_logger = "0.11" fd-lock = "4.0.0" flate2 = "1.0.26" hex = "0.4.3" indexmap = { version = "2.1.0", features = ["serde"] } log = "0.4.19" lz4_flex = "0.11.1" lzma-rs = "0.3.0" memchr = "2.5.0" nix = { version = "0.29", default-features = false, features = ["sched"] } peekread = "0.1.1" reqwest = { version = "0.12", features = ["rustls-tls-native-roots", "socks", "stream"], default-features = false } ruzstd = "0.7" # only the cert parser is used, but sequoia-openpgp doesn't allow building with no backend sequoia-openpgp = { version = "1.18", default-features = false, features = ["crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto"] } serde = { version = "1", features = ["derive"] } serde_json = "1" sha1 = "0.10.5" sha2 = "0.10.7" tar = "0.4.38" tempfile = "3.6.0" time = { version = "0.3", features = ["formatting"] } tokio = { version = "1", features = ["macros", "rt-multi-thread", "fs", "process", "signal"] } toml = "0.8" urlencoding = "2.1.2" repro-env-0.4.1/LICENSE-APACHE000064400000000000000000000261351046102023000134760ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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 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. repro-env-0.4.1/LICENSE-MIT000064400000000000000000000020401046102023000131730ustar 00000000000000MIT License Copyright (c) 2023 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. repro-env-0.4.1/Makefile000064400000000000000000000004551046102023000132070ustar 00000000000000build: cargo run --release -- build -- make build2 sha256sum target/x86_64-unknown-linux-musl/release/repro-env build2: RUSTFLAGS="-C strip=symbols" \ cargo build --target x86_64-unknown-linux-musl --release docs: docs/repro-env.1 docs/%: docs/%.scd scdoc < $^ > $@ .PHONY: build build2 docs repro-env-0.4.1/README.md000064400000000000000000000201601046102023000130210ustar 00000000000000# repro-env Imagine you had a tool that takes a config like this: ```toml # repro-env.toml [container] image = "rust:1-alpine3.18" ``` and turns it into something like this: ```toml # repro-env.lock [container] image = "rust@sha256:22760a18d52be83a74f5df8b190b8e9baa1e6ce7d9bda40630acc8ba5328a2fd" ``` You commit both into your git repository to document: - **repro-env.toml**: which container image tag you intend to follow (think `Cargo.toml`) - **repro-env.lock**: which specific image you use for your release build (think `Cargo.lock`) The .lock file is auto-generated and can be refreshed with a simple command: ``` repro-env update ``` The build is executed in a user-namespace with **podman** (make sure it's installed), the current directory is mounted to `/build/` and a given command is executed inside of that directory: ``` repro-env build -- cargo build ``` We want to distribute our binary without having to worry about system libraries, so we ask cargo to create static binaries (also enable release optimizations): ``` repro-env build -- cargo build --release --target x86_64-unknown-linux-musl ``` This way we also ensure a different build folder is used (`target/x86_64-unknown-linux-musl` instead of `target/`) so our normal development doesn't interfere. The final executable is available at this location: ``` ./target/x86_64-unknown-linux-musl/release/repro-env --help ``` ## Download - [repro-env x86_64 statically linked](https://github.com/kpcyrd/repro-env/releases/download/v0.4.1/repro-env) (sha256: `d6cefae67a91fc42546a2bae904fcb4bb169552229c11a328a0bc0becc212a6d`) [![](https://repology.org/badge/vertical-allrepos/repro-env.svg)](https://repology.org/project/repro-env/versions) With github actions: ```yaml - name: Install repro-env run: | wget 'https://github.com/kpcyrd/repro-env/releases/download/v0.4.1/repro-env' echo 'd6cefae67a91fc42546a2bae904fcb4bb169552229c11a328a0bc0becc212a6d repro-env' | sha256sum -c - sudo install -m755 repro-env -t /usr/bin ``` With github actions (>= Ubuntu 24.04): ```yaml - name: Install repro-env run: sudo apt-get install -y repro-env ``` | Package integration | Status | Archive infrastructure | | -------------------------------------- | ------ | ---------------------- | | [Arch Linux](#packages-arch-linux) | ✅ Fully supported, no known issues | ✅ Superb, operated by Arch Linux | | [Debian](#packages-debian) | ✅ No known issues | ⚠️ Snapshot service is frequently slow or unavailable | | [Alpine Linux](#packages-alpine-linux) | ✅ No known issues | ❌ No public archive, links are likely to become 404 | ## Packages: Arch Linux Arch Linux hosts a comprehensive collection of recent compilers at https://archive.archlinux.org. You can create a `[packages]` section in your **repro-env.toml** with `system = "archlinux"` to install additional packages with pacman. ```toml # repro-env.toml [container] image = "docker.io/library/archlinux" [packages] system = "archlinux" dependencies = ["rust-musl", "lua"] ``` The resolved **repro-env.lock** is going to contain the sha256 of the resolved container image you use as a base, and a list of `[[package]]` that should be installed/upgraded inside of the container before starting the build. ```toml # repro-env.lock [container] image = "docker.io/library/archlinux@sha256:6568d3f1f278827a4a7d8537f80c2ae36982829a0c6bccff4cec081774025472" # [...] [[package]] name = "rust" version = "1:1.69.0-3" system = "archlinux" url = "https://archive.archlinux.org/packages/r/rust/rust-1%3A1.69.0-3-x86_64.pkg.tar.zst" sha256 = "b8eb31a2eb80efab27bb68beab80436ed3e1d235a217c3e24ba973936c95839e" signature = "iIsEABYIADMWIQQGaHodnU+rCLUP2Ss7lKgOUKR3xwUCZExVKBUcaGVmdGlnQGFyY2hsaW51eC5vcmcACgkQO5SoDlCkd8fQkAD6AudRi2qP3WxSn38OOkSRSITciqRevPaVJgrz03JUBEAA/12h9z8dReD07Lqnltx9QTa3Cxppbv7VpJlTCQuavoMG" [[package]] name = "rust-musl" version = "1:1.69.0-3" system = "archlinux" url = "https://archive.archlinux.org/packages/r/rust-musl/rust-musl-1%3A1.69.0-3-x86_64.pkg.tar.zst" sha256 = "5a4854cdac8312dbf72fb87795bcc36bfb34e9218944966e5ac2e62319bbcf22" signature = "iIsEABYIADMWIQQGaHodnU+rCLUP2Ss7lKgOUKR3xwUCZExVKRUcaGVmdGlnQGFyY2hsaW51eC5vcmcACgkQO5SoDlCkd8cCMQD/W59RkOVPZDXlnmyY27jW61GC86hXOkSLOKa7XMQtpBoBALSugCkG1clSo/EQDbnuS+UY3268HNBvz6mF6i/hhEsB" ``` ## Packages: Debian Debian is a widely accepted choice and hosts an archive of all their packages at https://snapshot.debian.org/. You can create a `[packages]` section in your **repro-env.toml** with `system = "debian"` to install additional packages with apt-get. ```toml # repro-env.toml [container] image = "debian:bookworm" [packages] system = "debian" dependencies = ["gcc", "libc6-dev"] ``` Note this only works with **official** debian packages (not ubuntu). The resolved **repro-env.lock** is going to contain the sha256 of the resolved container image you use as a base, and a list of `[[package]]` that should be installed/upgraded inside of the container before starting the build. ```toml # repro-env.lock [container] image = "debian@sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b" [[package]] name = "binutils" version = "2.40-2" system = "debian" url = "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils_2.40-2_amd64.deb" sha256 = "83c3e20b53e1fbd84d764c3ba27d26a0376e361ae5d7fb37120196934dd87424" [[package]] name = "binutils-common" version = "2.40-2" system = "debian" url = "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils-common_2.40-2_amd64.deb" sha256 = "ab314134f43a0891a48f69a9bc33d825da748fa5e0ba2bebb7a5c491b026f1a0" # [...] ``` ## Packages: Alpine Linux Alpine is very popular in the container world, based on musl libc and has a wide selection of compilers in recent versions. You can create a `[packages]` section in your **repro-env.toml** with `system = "alpine"` to install additional packages with apk. Unfortunately there's currently no public archive of old Alpine packages, you should keep this in mind because your repro-env build environments **are likely to become uninstallable!** ```toml # repro-env.toml [container] image = "docker.io/library/alpine" [packages] system = "alpine" dependencies = ["gcc", "make", "musl-dev"] ``` The resolved **repro-env.lock** is going to contain the sha256 of the resolved container image you use as a base, and a list of `[[package]]` that should be installed/upgraded inside of the container before starting the build. ```toml # repro-env.lock [container] image = "docker.io/library/alpine@sha256:eece025e432126ce23f223450a0326fbebde39cdf496a85d8c016293fc851978" [[package]] name = "binutils" version = "2.40-r7" system = "alpine" url = "https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/binutils-2.40-r7.apk" sha256 = "6b1bf117b8f0a15862b27ff77a412eaccf2e7d8048a9cc0e3903e44930547c80" [[package]] name = "busybox" version = "1.36.1-r4" system = "alpine" url = "https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/busybox-1.36.1-r4.apk" sha256 = "abccb59dd5b9e64b782bbfd97b08c79a2214cc53567fb334aa003815505a007f" # [...] ``` ## Bootstrapping There are no inherent bootstrapping challenges, you can use any recent Rust compiler to build a working **repro-env** binary. This binary can then setup any other build environment (including it's own) and is able to build a bit-for-bit identical copy of the official release binaries hosted on github. ## Reproducible Builds All [pre-compiled binaries](https://github.com/kpcyrd/repro-env/releases) can be reproduced from source code: ```sh % wget https://github.com/kpcyrd/repro-env/releases/download/v0.4.1/repro-env [...] % sha256sum repro-env d6cefae67a91fc42546a2bae904fcb4bb169552229c11a328a0bc0becc212a6d repro-env ``` Since the build environment is fully documented and tracked in git all we need is checkout the corresponding git tag and run `make`: ```sh % git clone https://github.com/kpcyrd/repro-env % cd repro-env % git checkout v0.4.1 % make % sha256sum target/x86_64-unknown-linux-musl/release/repro-env d6cefae67a91fc42546a2bae904fcb4bb169552229c11a328a0bc0becc212a6d target/x86_64-unknown-linux-musl/release/repro-env ``` ## License `MIT OR Apache-2.0` repro-env-0.4.1/deny.toml000064400000000000000000000027671046102023000134130ustar 00000000000000# This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] ignore = [ { id = "RUSTSEC-2023-0071", reason = "this software never creates signatures" }, #"RUSTSEC-0000-0000", ] # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] allow = [ "Apache-2.0", "BSD-3-Clause", "BSL-1.0", "CC0-1.0", "ISC", "LGPL-2.0", "MIT", "MPL-2.0", "OpenSSL", "Unicode-3.0", "Unicode-DFS-2016", "Zlib", ] [[licenses.clarify]] crate = "ring" expression = "MIT AND ISC AND OpenSSL" license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html [bans] multiple-versions = "allow" wildcards = "allow" highlight = "all" workspace-default-features = "allow" external-default-features = "allow" # This section is considered when running `cargo deny check sources`. # More documentation about the 'sources' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html [sources] unknown-registry = "warn" unknown-git = "warn" allow-git = [] repro-env-0.4.1/docs/repro-env.1.scd000064400000000000000000000061031046102023000152420ustar 00000000000000REPRO-ENV(1) # NAME repro-env - Dependency lockfiles for reproducible build environments # SYNOPSIS *repro-env* update *repro-env* build -- [_COMMAND_] # DESCRIPTION Tracks a description of a desired state in *repro-env.toml*, for example, the latest version of some official container image, with the latest patch level and the latest version of some additional packages. It also tracks a resolved variant in *repro-env.lock* that tracks the specific versions and checksums of the packages needed to set up the described environment (at the patch-level available at the time of writing). The purpose of *repro-env.toml* is to make it trivial to re-resolve the specification if new patches become available. This file is read by *repro-env update*. The purpose of *repro-env.lock* is to document which compiler versions have been used for the release binary associated with a given release. Recording this information is essential for reproducible builds and allows future forensic investigation of the build environment. It can be either committed into the source-code repository or attached to a release as an artifact, along with the compiled binary. This file is read by *repro-env build*. # GLOBAL OPTIONS *-v*, *--verbose* Increase logging output (can be used multiple times) *-C* _path_, *--context* _path_ Change the current directory to this path before executing the subcommand # UPDATE This command resolves the environment described in *repro-env.toml* with the latest available updates and writes a *repro-env.lock*. You would use this command similar to how you would use *cargo update*. *--no-pull* Do not attempt to pull the container tag from registry before resolving it *-k*, *--keep* Do not delete the build container, wait for ctrl-c # BUILD This command loads a *repro-env.lock*, sets up the environment it describes in a container and mounts the current directory to */build* inside of the container. It then runs the given _COMMAND_ inside of this container. *-f* _path_, --file _path_ The dependency lockfile to use *-k*, *--keep* Do not delete the build container, wait for ctrl-c *-e* _env_, **--env** _env_ Pass environment variables into the build container (FOO=bar or just FOO to lookup the value) # PACKAGES: ARCH LINUX Arch Linux hosts a comprehensive collection of recent compilers at https://archive.archlinux.org. You can create a *[packages]* section in your *repro-env.toml* with *system = "archlinux"* to install additional packages with pacman. ``` # repro-env.toml [container] image = "docker.io/library/archlinux" [packages] system = "archlinux" dependencies = ["rust-musl", "lua"] ``` # PACKAGES: DEBIAN Debian is a widely accepted choice and hosts an archive of all their packages at https://snapshot.debian.org/. You can create a *[packages]* section in your *repro-env.toml* with *system = "debian"* to install additional packages with apt-get. ``` # repro-env.toml [container] image = "debian:bookworm" [packages] system = "debian" dependencies = ["gcc", "libc6-dev"] ``` # AUTHORS repro-env is developed on github at https://github.com/kpcyrd/repro-env repro-env-0.4.1/flake.lock000064400000000000000000000074131046102023000135040ustar 00000000000000{ "nodes": { "fenix": { "inputs": { "nixpkgs": "nixpkgs", "rust-analyzer-src": "rust-analyzer-src" }, "locked": { "lastModified": 1708150887, "narHash": "sha256-lyEaeShLZqQtFO+ULLfxF9fYaYpTal0Ck1B+iKYBOMs=", "owner": "nix-community", "repo": "fenix", "rev": "761431323e30846bae160e15682cfa687c200606", "type": "github" }, "original": { "owner": "nix-community", "repo": "fenix", "type": "github" } }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { "lastModified": 1705309234, "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { "owner": "numtide", "repo": "flake-utils", "type": "github" } }, "naersk": { "inputs": { "nixpkgs": "nixpkgs_2" }, "locked": { "lastModified": 1698420672, "narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=", "owner": "nix-community", "repo": "naersk", "rev": "aeb58d5e8faead8980a807c840232697982d47b9", "type": "github" }, "original": { "owner": "nix-community", "repo": "naersk", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1707956935, "narHash": "sha256-ZL2TrjVsiFNKOYwYQozpbvQSwvtV/3Me7Zwhmdsfyu4=", "owner": "nixos", "repo": "nixpkgs", "rev": "a4d4fe8c5002202493e87ec8dbc91335ff55552c", "type": "github" }, "original": { "owner": "nixos", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { "lastModified": 1708151420, "narHash": "sha256-MGT/4aGCWQPQiu6COqJdCj9kSpLPiShgbwpbC38YXC8=", "owner": "NixOS", "repo": "nixpkgs", "rev": "6e2f00c83911461438301db0dba5281197fe4b3a", "type": "github" }, "original": { "id": "nixpkgs", "type": "indirect" } }, "nixpkgs_3": { "locked": { "lastModified": 1708151420, "narHash": "sha256-MGT/4aGCWQPQiu6COqJdCj9kSpLPiShgbwpbC38YXC8=", "owner": "NixOS", "repo": "nixpkgs", "rev": "6e2f00c83911461438301db0dba5281197fe4b3a", "type": "github" }, "original": { "owner": "NixOS", "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "fenix": "fenix", "flake-utils": "flake-utils", "naersk": "naersk", "nixpkgs": "nixpkgs_3" } }, "rust-analyzer-src": { "flake": false, "locked": { "lastModified": 1708018577, "narHash": "sha256-B75VUqKvQeIqAUnYw4bGjY3xxrCqzRBJHLbmD0MAWEw=", "owner": "rust-lang", "repo": "rust-analyzer", "rev": "b9b0d29b8e69b02457cfabe20c4c69cdb45f3cc5", "type": "github" }, "original": { "owner": "rust-lang", "ref": "nightly", "repo": "rust-analyzer", "type": "github" } }, "systems": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "owner": "nix-systems", "repo": "default", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "type": "github" }, "original": { "owner": "nix-systems", "repo": "default", "type": "github" } } }, "root": "root", "version": 7 } repro-env-0.4.1/flake.nix000064400000000000000000000020371046102023000133470ustar 00000000000000{ inputs = { fenix.url = "github:nix-community/fenix"; flake-utils.url = "github:numtide/flake-utils"; naersk.url = "github:nix-community/naersk"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; }; outputs = { self, fenix, flake-utils, naersk, nixpkgs }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = (import nixpkgs) { inherit system; }; toolchain = with fenix.packages.${system}; combine [ stable.rustc stable.cargo targets.x86_64-unknown-linux-musl.stable.rust-std ]; naersk' = naersk.lib.${system}.override { cargo = toolchain; rustc = toolchain; }; in rec { defaultPackage = naersk'.buildPackage { src = ./.; nativeBuildInputs = with pkgs; [ pkgsStatic.stdenv.cc ]; CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static -C strip=symbols"; }; } ); } repro-env-0.4.1/repro-env.lock000064400000000000000000000204331046102023000143340ustar 00000000000000[container] image = "docker.io/library/archlinux@sha256:486969816908ee2135d869bd7d3ca9bd15dc49f755e06f2d212915992b25b29b" [[package]] name = "binutils" version = "2.42+r195+g29ae8b8ea71-1" system = "archlinux" url = "https://archive.archlinux.org/packages/b/binutils/binutils-2.42+r195+g29ae8b8ea71-1-x86_64.pkg.tar.zst" sha256 = "a404e22d3ae833b3a662c35994f566a6bbbe1a9d27ac61e10c6e905584c3c30c" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZp6OZV8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCmyCAP45yJllhHSkwGXMqvpDHWO3tEBm0xAYlz7z8Uy2ijC3VQEAuqsah6z+EkA8gCZn5vRg0hXsBwqPIPTDkxwTRwXnggg=" [[package]] name = "gc" version = "8.2.6-1" system = "archlinux" url = "https://archive.archlinux.org/packages/g/gc/gc-8.2.6-1-x86_64.pkg.tar.zst" sha256 = "6210a1e7e00d3162f175f9ab318a3da928495210b1ea411dcb66bf5e1f382daf" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZb9oiF8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCmQdAP0fleaFihboUqrAdlMxvO5OUJiXXxLtimhQj2e3rD9L3gEA0YzMCzi4GS6GlV1pa/hZ68bPL2gYz2goni9/aLXhMwY=" [[package]] name = "gcc" version = "14.1.1+r309+gbb34b7eda1f-1" system = "archlinux" url = "https://archive.archlinux.org/packages/g/gcc/gcc-14.1.1+r309+gbb34b7eda1f-1-x86_64.pkg.tar.zst" sha256 = "15ea30e5e8592e4caa7383d6e13690f566a3fe71bc1b160cfc25cc133e04873b" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZp6Oal8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCqBRAQC+Bi6H36krl2JconXbBYGzY5b2lwI3uYjGsSsumFl6vAD/YLleu6obbmQ6ba+vR8N8tz30Knr+VtUja9GSJbEvigY=" [[package]] name = "guile" version = "3.0.10-1" system = "archlinux" url = "https://archive.archlinux.org/packages/g/guile/guile-3.0.10-1-x86_64.pkg.tar.zst" sha256 = "bf4d1b474045a88c7ad97b1ce56e8dd5786e5a10de02a8d33dc8f041edc8d01d" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZnkshF8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCruHAQDnjJsHGhq4cae34rHS1z+Hr6yzjVQSxvA6SE9XpredlAD/cstobLSMUsHswxf5df94XkoDPJdJs44uXE/iu9gsgQg=" [[package]] name = "jansson" version = "2.14-4" system = "archlinux" url = "https://archive.archlinux.org/packages/j/jansson/jansson-2.14-4-x86_64.pkg.tar.zst" sha256 = "a67ab57d4a9b4caa2e718652c392345cdd9c2496d835ab1d0ff1e78ae4429fde" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZjJful8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCpfCAP4sFnTjSEIn5wDbLWGZOivsWT2SWPG6AgofrnUjaK/UMAD9E2u6D7WJbVTYrvDhVUz3WN6k8bVB8ZODyoIF66GWEw0=" [[package]] name = "libedit" version = "20240517_3.1-1" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libedit/libedit-20240517_3.1-1-x86_64.pkg.tar.zst" sha256 = "fa17f759180233be8343ccccb1c3e4ac01cf5367ab141f36ca8830151b7c12be" signature = "iQIzBAABCgAdFiEE4kC1fixGMLp2ji8m/BtUfI2BcsgFAmZRDMAACgkQ/BtUfI2BcsjfBg/9GJ/xfxU7NKehazazuulK0VWaeXwc2SydHS4lpnqZggkcNtVlJIXzIrIBAK+A06M99NLqJzdrLsCssa5RjlMSxtB8nZYjZIE7M4fivBHgx4N6xUzzSu7PN8G9A1fvnbRFqtcvywP/FeWnmBIsf404BeKhwHB0PkG9RebYmJAKpg1IHhHNBJ/NkYC04Wov4oskzv/HehibIbnvTu6EZQ2f2NyXJVTqUsEkEw5xSEsyob+fgAtMdfufs6xpFskYV9NWniHKxWN9JKy0HwJ764JrxRW5VrrR2Ua/zWQYBi0r71zfrt/mILjkJwWNe0+LzJWpOqD59sjllW2iBeTeH+0A4lG7beaKdhCAAQQUzAPINuCwhpOGvvFhOn04icVPgqFKVegbNbXN+Wad+60HKgQw4AI0XeHo8uTiJOKolObV27OxaQTK2s5qmsrali4zsLlC+G2lYUvrqvWlrtuIIm+6XGNSMDoYovVd2EoBGRrwFCDlfdah8kFa6PY9J1UWQY7mDPJerpgB0IZ0KycxpXvB5I5XzYLKbSEjWZ331jnCWbR+poQfYwMYI2H9ZFy2YN0R/VohaIU70Ux5F24gjUaLYc1Ld9ZzrV1MSMj+D20vdxLCmNoDLfNwwceBbCyisT5GnxAFVWvaX3WwhI/D/jPnBcuEw11FfsHLUaGNIfM+mQQ=" [[package]] name = "libisl" version = "0.26-2" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libisl/libisl-0.26-2-x86_64.pkg.tar.zst" sha256 = "c9e75e062e0c63ea4ce8ff3b74ed09400c58e70323362ea98039e8ca89d7dce3" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZjJful8UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCkLXAQC49BDxyJqogwtJnhf2IuViUWKWh8D9STZdlmc27Kwy0wEA3at28YnTA1thVrPW3SmnqteeRH+CEwIh7klsYADidAs=" [[package]] name = "libmpc" version = "1.3.1-2" system = "archlinux" url = "https://archive.archlinux.org/packages/l/libmpc/libmpc-1.3.1-2-x86_64.pkg.tar.zst" sha256 = "10554706eb15ed2186fbd55fd5db8fa5919788ac9a75d26c0b9ef5bdfca9d470" signature = "iQEzBAABCAAdFiEEFRnVq6Zb9vwrc8dWek52CV2KUuQFAmaHCZwACgkQek52CV2KUuRjbAf/TmL9cCMXYTGsNmR2om6kT7XjczfZoFR9AM0FSTUNy5AuNZNUUCfm6ie6F4rFwHc3nETOSojScOZauZlcNiLrVriHYYFSZ8EhKiCJCo68a7KTbceBNoBuT6AEIpGMpIMti/cvWhu8VXl5JIRo8LNPBzDih05aCDUJ2nayKPYNHalDayu87sDXzJYXuzC8KD7XmUa7Kirn0ZpA4VZXc8CUjYYvn0wvRwLKQP4WMpszxmblQWjMVbQxh7dwC+gPtqIANVjoGKMRfr6QpT03XXIEaTBKbMsxeYFvwn+y2JN30t6oFI6LJADZ22VwimEzu06u+x9c+3+pr4k7Py43OCOGmg==" [[package]] name = "licenses" version = "20240728-1" system = "archlinux" url = "https://archive.archlinux.org/packages/l/licenses/licenses-20240728-1-any.pkg.tar.zst" sha256 = "47e9f4ad914da21f6326cc4eb84a788043523191f80d8e661b936b25d7fa1879" signature = "iHUEABYKAB0WIQRizHP4hOUpV7L92IObeih9mi7GCAUCZqXzbQAKCRCbeih9mi7GCLu9APsH+bwAgFsWaeJ3K+YiWgqZLEl6hFkfwDqeXkSwtWDY6QEA2ja8vHvDA61PIS/Jlj2Fc9HifdwKBpzeEElsU0BP5Q8=" [[package]] name = "llvm-libs" version = "18.1.8-4" system = "archlinux" url = "https://archive.archlinux.org/packages/l/llvm-libs/llvm-libs-18.1.8-4-x86_64.pkg.tar.zst" sha256 = "bb417925759c69584e557666528c728af776b8e815cdd45d3125c725ddf8d9db" signature = "iQEzBAABCAAdFiEEhs/8qRjPOvRxR1iAUeixSKmZnDQFAmaYGg4ACgkQUeixSKmZnDR4ygf/S2itjSyaPvaiabUUZulYP+rlyQb8hxgYKfAIEBYU1xHRIYBqKuy+9VQMF7o57Jgawbdpk6wRgf2h5rvMj9LY/fpkaPCbK/t13c2zmEAROihuXLTGd6DJwhH9aGVAdDtCs1Nn3w6BnJI78buUCAEi0OGjxOC6RQiAUlPk7g8TKixvJiVgREyrm72TpFeaxzD2UuCw9X0Jj3rlFqmWNqjxoKfQptPUYk2pLZhcJAzFw0tZTd/5zCM+9/gE2AAd0C4lBEF/Xi37RSI3Y80oYFCL91b2x37S1XlYZDAXN/MZqOABCfMXI41HHL8wUX6xARqcTRvaMk3922f2oMZ8ISQyYg==" [[package]] name = "make" version = "4.4.1-2" system = "archlinux" url = "https://archive.archlinux.org/packages/m/make/make-4.4.1-2-x86_64.pkg.tar.zst" sha256 = "7c1bc0d882f7c8d1bcb305eb7efaabc19ed6afa5a9a443575fa0d5ed57985535" signature = "iHUEABYKAB0WIQSZH24/B2XPYpWIhYYTmwnaW/DTOAUCZBT0lQAKCRATmwnaW/DTOKwJAP9/kfT3rO0NhbD8wuI6ajzjyKtrl4SfuU0yu/PKByXQOgD/aYSvsyXylJhCadU2smO4LbP2Vj9fnIvEwPvbXbesVQ0=" [[package]] name = "musl" version = "1.2.5-2" system = "archlinux" url = "https://archive.archlinux.org/packages/m/musl/musl-1.2.5-2-x86_64.pkg.tar.zst" sha256 = "146b60543069d4eb92fd9086ffd6f442ee0a1f41f724445adc29af80693ce2da" signature = "iQJLBAABCAA1FiEEiee5MxxK59f699MFwTIpOVS75K0FAmaUdzsXHHNwdXB5a2luQGFyY2hsaW51eC5vcmcACgkQwTIpOVS75K2C+g//ddIjb7CKai01nuuzLd9hZQBP51iBe+lYdhv5lxlKaSzQZXHVUGXDAZdb7TaV9pbzdGPE+l5V2ZngivXdR5twUDWOQzvorXdxLgX7trBTghLDTBIKu6jPtrH2bNWrL8A908RumQVeWsOxUcompgAsRpQ4DhZkzk8325k9et0Nv1oQic96B5G1WefE85IYc2PDJeRhfyfnZmQ15aoVhohyhF0XSudlJSc/hqMZePW8tmyYz4ltvgOs4EF3smNwOaZVOew+w/5vOCYaSre5MFwHiilvYJaWhCji10m0MrBzLhRx0ZfRlBwTGbsToRbmczuwMXoYIiJsI2OiXsZD5/X9T5yPjl7pWP7uLaNTXCcbfrCZIWMVBXYQFxnWhUbKFZu8E7eXU2z75GukkC7GjR+VKw+SFH06Xdq73JltIXURRKe03uigC8ciME1xHOMv5kc1pQukQC/hTaR4GPF32pOX27CPQkNQ0dhRi1Cgyt47V+3GncI+mhlY3gJsCv31+Z0kM+WornVJYFSQnO0/frFPJaZUt0ONO7lHU2UMTe2tHm+zFHuHh6xOGaMjdRslrzq0Pby0xMb6nT0HsT1VXfSW30rywN1UPLd3TxAXGkG3SS8szftrLrmK6NAsK5tWfd5H2tiwSibYNnYYYVB720LKBl7bgpGFicdROohXJKqXsvA=" [[package]] name = "rust" version = "1:1.80.0-1" system = "archlinux" url = "https://archive.archlinux.org/packages/r/rust/rust-1:1.80.0-1-x86_64.pkg.tar.zst" sha256 = "3a0b4f61d188a7fe39707fc55487a4279cea5140dc11b708e588718f4b071c67" signature = "iHUEABYKAB0WIQSDvIiJNRtd67toQW64rAhgDxCM3wUCZqMGBQAKCRC4rAhgDxCM3+QJAP9FBsl2FQ6RpmhsBxC/0gdaGKqL0EcUS1sUGhVAVjDM+AEAvqJgoZRqKnUhvojHTMzGQcHKRN+EC8JIZnDg4BcYrwQ=" [[package]] name = "rust-musl" version = "1:1.80.0-1" system = "archlinux" url = "https://archive.archlinux.org/packages/r/rust-musl/rust-musl-1:1.80.0-1-x86_64.pkg.tar.zst" sha256 = "d382bc3ed1257f5ea074c4496c96fd9d1eb028343980c6001a370ec8b520cb21" signature = "iHUEABYKAB0WIQSDvIiJNRtd67toQW64rAhgDxCM3wUCZqMGCAAKCRC4rAhgDxCM3zrDAP0Rg0VNUp8+GHENTvnXLSZKJYD+G15hky7WiJKvdq8vRgEA6UmF3e2wsnINM99EQfasUdYVd054bBK0ruxoTb5jbwY=" repro-env-0.4.1/repro-env.toml000064400000000000000000000002001046102023000143450ustar 00000000000000[container] image = "docker.io/library/archlinux" [packages] system = "archlinux" dependencies = ["make", "musl", "rust-musl"] repro-env-0.4.1/src/args.rs000064400000000000000000000066521046102023000136450ustar 00000000000000use crate::errors::*; use crate::lockfile::Lockfile; use crate::manifest::Manifest; use clap::{ArgAction, CommandFactory, Parser, Subcommand}; use clap_complete::Shell; use std::collections::HashSet; use std::env; use std::io; use std::path::Path; use std::path::PathBuf; #[derive(Debug, Parser)] #[command(version)] pub struct Args { /// Increase logging output (can be used multiple times) #[arg(short, long, global = true, action(ArgAction::Count))] pub verbose: u8, /// Change the current directory to this path before executing the subcommand #[arg(short = 'C', long)] pub context: Option, #[command(subcommand)] pub subcommand: SubCommand, } #[derive(Debug, Subcommand)] pub enum SubCommand { Build(Build), Update(Update), Fetch(Fetch), Completions(Completions), } /// Run a build in a reproducible environment #[derive(Debug, Parser)] pub struct Build { /// The dependency lockfile to use #[arg(short, long)] pub file: Option, /// Do not delete the build container, wait for ctrl-c #[arg(short, long)] pub keep: bool, /// Pass environment variables into the build container (FOO=bar or just FOO to lookup the value) #[arg(short, long)] pub env: Vec, /// The command to execute inside the build container #[arg(required = true)] pub cmd: Vec, } impl Build { pub fn validate(&self) -> Result<()> { let mut env_keys = HashSet::new(); for env in &self.env { let key = if let Some((key, _value)) = env.split_once('=') { key } else if env::var(env).is_ok() { env } else { bail!("Referenced environment variables does not exist: {env:?}"); }; if !env_keys.insert(key) { bail!("Can not set environment multiple times: {key:?}"); } } Ok(()) } pub async fn load_files(&self) -> Result<(Option, Lockfile)> { let path = self.file.as_deref().unwrap_or(Path::new("repro-env.lock")); let lockfile = Lockfile::read_from_file(path).await?; let manifest = if self.file.is_none() { Some(Manifest::read_from_file("repro-env.toml").await?) } else { None }; Ok((manifest, lockfile)) } } /// Update all dependencies of the reproducible environment #[derive(Debug, Parser)] pub struct Update { /// Do not attempt to pull the container tag from registry before resolving it #[arg(long)] pub no_pull: bool, /// Do not delete the build container, wait for ctrl-c #[arg(short, long)] pub keep: bool, } /// Fetch dependencies into the local cache #[derive(Debug, Parser)] pub struct Fetch { /// The dependency lockfile to use #[arg(short, long)] pub file: Option, /// Do not attempt to pull the container tag from registry #[arg(long)] pub no_pull: bool, } /// Generate shell completions #[derive(Debug, Parser)] pub struct Completions { pub shell: Shell, } impl Completions { pub fn generate(&self, mut w: W) -> Result<()> { clap_complete::generate(self.shell, &mut Args::command(), "repro-env", &mut w); Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_zsh_completions() { Completions { shell: Shell::Zsh } .generate(io::sink()) .unwrap(); } } repro-env-0.4.1/src/build.rs000064400000000000000000000172741046102023000140120ustar 00000000000000use crate::args; use crate::container::{self, Container}; use crate::errors::*; use crate::fetch; use crate::lockfile::PackageLock; use crate::paths; use crate::pgp; use crate::pkgs::archlinux; use data_encoding::BASE64; use std::env; use std::path::Path; use std::time::Duration; use tempfile::TempDir; use time::format_description::well_known; use time::OffsetDateTime; use tokio::fs; #[derive(Debug, PartialEq, Default)] pub struct Install { alpine: Vec<(PackageLock, String)>, archlinux: Vec<(PackageLock, String)>, debian: Vec<(PackageLock, String)>, } impl Install { fn add_pkg(&mut self, pkg: PackageLock, filename: String) -> Result<()> { let list = match pkg.system.as_str() { "alpine" => &mut self.alpine, "archlinux" => &mut self.archlinux, "debian" => &mut self.debian, system => bail!("Unknown package system: {system:?}"), }; list.push((pkg, filename)); Ok(()) } } pub async fn setup_extra_folder(path: &Path, dependencies: Vec) -> Result { let pkgs_cache_dir = paths::pkgs_cache_dir()?; let mut install = Install::default(); for package in dependencies { // determine filename let url = package .url .parse::() .with_context(|| anyhow!("Failed to parse string as url: {:?}", package.url))?; let filename = url .path_segments() .context("Failed to get path from url")? .last() .context("Failed to find filename from url")?; if filename.is_empty() { bail!("Filename from url is empty"); } // setup /extra/ directory let source = pkgs_cache_dir.sha256_path(&package.sha256)?; let dest = path.join(filename); let dest_sig = path.join(filename.to_owned() + ".sig"); debug!("Trying to reflink {source:?} -> {dest:?}..."); if let Err(err) = clone_file::clone_file(&source, &dest) { debug!("Failed to reflink, trying traditional copy: {err:#}"); fs::copy(&source, &dest) .await .context("Failed to copy package from cache to temporary folder")?; } // setup extra data match package.system.as_str() { "alpine" => (), "archlinux" => { let base64 = package .signature .as_ref() .context("Package in dependency lockfile is missing signature")?; let signature = BASE64 .decode(base64.as_bytes()) .with_context(|| anyhow!("Failed to decode signature as base64: {base64:?}"))?; debug!( "Writing signature ({} bytes) to {dest_sig:?}...", signature.len() ); fs::write(dest_sig, signature).await?; } "debian" => (), system => bail!("Unknown package system: {system:?}"), } // verify pkg content matches pin metadata let pkg = fs::read(&dest).await?; fetch::verify_pin_metadata(&pkg, &package) .with_context(|| anyhow!("Failed to verify metadata for {filename:?}"))?; install.add_pkg(package, filename.to_string())?; } Ok(install) } pub async fn run_build( container: &Container, build: &args::Build, extra: Option<&(TempDir, Install)>, ) -> Result<()> { if let Some((_, install)) = extra { if !install.alpine.is_empty() { let mut cmd = vec![ "apk".to_string(), "add".to_string(), "--no-network".to_string(), "--".to_string(), ]; for (_, filename) in &install.alpine { cmd.push(format!("/extra/{filename}")); } info!("Installing dependencies..."); container.exec(&cmd, container::Exec::default()).await?; } if !install.archlinux.is_empty() { // determine verification timestamp and add it to gpg.conf let filename_iter = install.archlinux.iter().map(|(pkg, _)| pkg); if let Some(time) = pgp::find_max_signature_time(filename_iter)? { let time = time .checked_add(Duration::from_secs(1)) .with_context(|| anyhow!("Failed to increase time by 1 second {time:?}"))?; let datetime = OffsetDateTime::from(time).format(&well_known::Rfc3339)?; info!("Derived signature verification timestamp: {datetime:?}"); archlinux::set_pacman_verification_datetime(container, time).await?; } // prepare and execute the install command let mut cmd = vec![ "pacman".to_string(), "-U".to_string(), "--noconfirm".to_string(), "--".to_string(), ]; for (_, filename) in &install.archlinux { cmd.push(format!("/extra/{filename}")); } info!("Installing dependencies..."); container.exec(&cmd, container::Exec::default()).await?; } if !install.debian.is_empty() { let mut cmd = vec![ "apt-get".to_string(), "install".to_string(), "--".to_string(), ]; for (_, filename) in &install.debian { cmd.push(format!("/extra/{filename}")); } info!("Installing dependencies..."); container.exec(&cmd, container::Exec::default()).await?; } } info!("Running build..."); container .exec( &build.cmd, container::Exec { cwd: Some("/build"), env: &build.env, ..Default::default() }, ) .await?; Ok(()) } pub async fn build(build: &args::Build) -> Result<()> { container::test_for_unprivileged_userns_clone().await?; // ensure arguments make sense build.validate()?; // load lockfile let (manifest, lockfile) = build.load_files().await?; if let Some(manifest) = &manifest { if let Err(err) = manifest.satisfied_by(&lockfile) { warn!("Lockfile might be out-of-sync: {err:#}"); } } // mount current directory into container let pwd = env::current_dir()?; let pwd = pwd .into_os_string() .into_string() .map_err(|_| anyhow!("Failed to convert current path to utf-8"))?; let mut mounts = vec![(pwd, "/build".to_string())]; // ignore packages that are already present in the container let dependencies = lockfile .packages .into_iter() .filter(|p| !p.installed) .collect::>(); let extra = if !dependencies.is_empty() { fetch::download_dependencies(&dependencies).await?; let path = paths::repro_env_dir()?; let temp_dir = tempfile::Builder::new().prefix("env.").tempdir_in(path)?; let pkgs = setup_extra_folder(temp_dir.path(), dependencies).await?; let path = temp_dir .path() .to_owned() .into_os_string() .into_string() .map_err(|_| anyhow!("Failed to convert temporary path to utf-8"))?; mounts.push((path, "/extra".to_string())); Some((temp_dir, pkgs)) } else { None }; let container = Container::create( &lockfile.container.image, container::Config { mounts: &mounts, expose_fuse: false, }, ) .await?; container .run(run_build(&container, build, extra.as_ref()), build.keep) .await } repro-env-0.4.1/src/container.rs000064400000000000000000000306641046102023000146730ustar 00000000000000use crate::errors::*; use serde::{Deserialize, Serialize}; use std::ffi::OsStr; use std::fmt; use std::future::{self, Future}; use std::io::Read; use std::process::Stdio; use std::str::FromStr; use tokio::io::AsyncWriteExt; use tokio::process::Command; use tokio::signal; #[derive(Debug, PartialEq, Clone)] pub struct ImageRef { pub repo: String, pub tag: Option, pub digest: Option, } impl FromStr for ImageRef { type Err = Error; fn from_str(s: &str) -> Result { if let Some((repo, digest)) = s.split_once('@') { Ok(ImageRef { repo: repo.to_string(), tag: None, digest: Some(digest.to_string()), }) } else if let Some((repo, tag)) = s.split_once(':') { Ok(ImageRef { repo: repo.to_string(), tag: Some(tag.to_string()), digest: None, }) } else { Ok(ImageRef { repo: s.to_string(), tag: None, digest: None, }) } } } impl fmt::Display for ImageRef { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { let repo = &self.repo; if let Some(digest) = &self.digest { write!(w, "{repo}@{digest}") } else if let Some(tag) = &self.tag { write!(w, "{repo}:{tag}") } else { write!(w, "{repo}") } } } #[derive(Debug, Default)] pub struct ExecConfig { pub capture_stdout: bool, pub silence_stderr: bool, pub stdin: Option>, } pub async fn podman(args: I, config: &ExecConfig) -> Result> where I: IntoIterator, S: AsRef + fmt::Debug, { let mut cmd = Command::new("podman"); let args = args.into_iter().collect::>(); cmd.args(&args); if config.stdin.is_some() { cmd.stdin(Stdio::piped()); } if config.capture_stdout { cmd.stdout(Stdio::piped()); } if config.silence_stderr { cmd.stderr(Stdio::null()); } debug!("Spawning child process: podman {:?}", args); let mut child = cmd.spawn().context("Failed to execute podman binary")?; // write to stdin (if configured) if let Some(buf) = &config.stdin { if let Some(mut stdin) = child.stdin.take() { stdin.write_all(buf).await?; } } // wait for the process to exit let out = child.wait_with_output().await?; debug!("Podman command exited: {:?}", out.status); if !out.status.success() { bail!( "Podman command ({:?}) failed to execute: {:?}", args, out.status ); } Ok(out.stdout) } pub async fn pull(image: &str) -> Result<()> { podman(&["image", "pull", "--", image], &ExecConfig::default()).await?; Ok(()) } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct Image { pub digest: String, } pub async fn inspect(image: &str) -> Result { let inspect = podman( &["image", "inspect", "--", image], &ExecConfig { capture_stdout: true, silence_stderr: true, ..Default::default() }, ) .await?; let mut list = serde_json::from_slice::>(&inspect)?; debug!("Image inspect result: {list:?}"); let inspect = list .pop() .with_context(|| anyhow!("Could not find any matching image: {image:?}"))?; match list.len() { 0 => Ok(inspect), len => bail!( "The specified image is not canonical, inspect returned {}, expected 1", len + 1 ), } } #[derive(Debug)] pub struct Config<'a> { pub mounts: &'a [(String, String)], pub expose_fuse: bool, } #[derive(Debug, Default)] pub struct Exec<'a> { pub capture_stdout: bool, pub cwd: Option<&'a str>, pub user: Option<&'a str>, pub env: &'a [String], } #[derive(Debug)] pub struct Container { pub id: String, } impl Container { pub async fn create(image: &str, config: Config<'_>) -> Result { let mut podman_args = vec![ "container".to_string(), "run".to_string(), "--detach".to_string(), "--rm".to_string(), "--network=host".to_string(), "-v=/usr/bin/catatonit:/__:ro".to_string(), "--entrypoint=/__".to_string(), ]; for (src, dest) in config.mounts { podman_args.push(format!("-v={src}:{dest}")); } if config.expose_fuse { debug!("Mapping /dev/fuse into the container"); podman_args.push("--device=/dev/fuse".to_string()); } podman_args.extend(["--".to_string(), image.to_string(), "-P".to_string()]); debug!("Creating container..."); let mut out = podman( &podman_args, &ExecConfig { capture_stdout: true, ..Default::default() }, ) .await?; if let Some(idx) = memchr::memchr(b'\n', &out) { out.truncate(idx); } let id = String::from_utf8(out)?; Ok(Container { id }) } pub async fn exec(&self, args: I, options: Exec<'_>) -> Result> where I: IntoIterator, S: AsRef + fmt::Debug + Clone, { let args = args.into_iter().collect::>(); let mut a = vec!["container".to_string(), "exec".to_string()]; if let Some(cwd) = options.cwd { a.extend(["-w".to_string(), cwd.to_string()]); } if let Some(user) = options.user { a.extend(["-u".to_string(), user.to_string()]); } for env in options.env { a.extend(["-e".to_string(), env.to_string()]); } a.extend(["--".to_string(), self.id.to_string()]); a.extend(args.iter().map(|x| x.as_ref().to_string())); let buf = podman( &a, &ExecConfig { capture_stdout: options.capture_stdout, ..Default::default() }, ) .await .with_context(|| anyhow!("Failed to execute in container: {:?}", args))?; Ok(buf) } pub async fn tar(&self, path: &str) -> Result> { let a = vec![ "container".to_string(), "cp".to_string(), "--".to_string(), format!("{}:{}", self.id, path), "-".to_string(), ]; let buf = podman( &a, &ExecConfig { capture_stdout: true, ..Default::default() }, ) .await .with_context(|| anyhow!("Failed to read from container: {:?}", path))?; Ok(buf) } pub async fn cat(&self, path: &str) -> Result> { let buf = self.tar(path).await?; let mut tar = tar::Archive::new(&buf[..]); let mut entries = tar.entries()?; let entry = entries .next() .context("Tar archive generated by podman cp is empty")?; let mut entry = entry?; let entry_type = entry.header().entry_type(); if entry_type != tar::EntryType::Regular { bail!("Extracted file is not of type file: {entry_type:?}"); } let mut buf = Vec::new(); entry.read_to_end(&mut buf)?; Ok(buf) } pub async fn write_file(&self, directory: &str, filename: &str, content: &[u8]) -> Result<()> { // generate tar file let mut tar = tar::Builder::new(Vec::new()); let mut header = tar::Header::new_gnu(); header.set_size(content.len() as u64); header.set_mode(0o640); debug!( "Adding to archive: {:?} ({} bytes)", filename, content.len() ); tar.append_data(&mut header, filename, content)?; let buf = tar.into_inner()?; // pass archive into container let a = vec![ "container".to_string(), "cp".to_string(), "--".to_string(), "-".to_string(), format!("{}:{}", self.id, directory), ]; podman( &a, &ExecConfig { stdin: Some(buf), ..Default::default() }, ) .await .with_context(|| { anyhow!("Failed to write container (directory={directory:?}, filename={filename:?}") })?; Ok(()) } pub async fn kill(&self) -> Result<()> { podman( &["container", "kill", &self.id], &ExecConfig { capture_stdout: true, ..Default::default() }, ) .await .context("Failed to remove container")?; Ok(()) } pub async fn run>>(&self, fut: F, keep: bool) -> Result<()> { let fut = async { fut.await?; if keep { info!("Keeping container around until ^C..."); future::pending().await } else { Ok(()) } }; let result = tokio::select! { result = fut => result, _ = signal::ctrl_c() => Err(anyhow!("Ctrl-c received")), }; debug!("Removing container..."); if let Err(err) = self.kill().await { warn!("Failed to kill container {:?}: {:#}", self.id, err); } debug!("Container cleanup complete"); result } } #[cfg(target_os = "linux")] pub fn test_userns_clone() -> Result<()> { use nix::sched::CloneFlags; use nix::sys::wait::{WaitPidFlag, WaitStatus}; let cb = Box::new(|| 0); let stack = &mut [0; 1024]; let flags = CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER; let pid = unsafe { nix::sched::clone(cb, stack, flags, None) } .context("Failed to create user namespace")?; let status = nix::sys::wait::waitpid(pid, Some(WaitPidFlag::__WCLONE)) .context("Failed to reap child")?; if status != WaitStatus::Exited(pid, 0) { bail!("Unexpected wait result: {:?}", status); } Ok(()) } #[cfg(target_os = "linux")] pub async fn test_for_unprivileged_userns_clone() -> Result<()> { if std::env::var("REPRO_ENV_SKIP_CLONE_CHECK") .map(|x| x != "0") .unwrap_or(false) { debug!("Skipping test if user namespaces can be created"); return Ok(()); } debug!("Testing if user namespaces can be created"); if let Err(err) = test_userns_clone() { match tokio::fs::read("/proc/sys/kernel/unprivileged_userns_clone").await { Ok(buf) => { if buf == b"0\n" { warn!("User namespaces are not enabled in /proc/sys/kernel/unprivileged_userns_clone") } } Err(err) => warn!( "Failed to check if unprivileged_userns_clone are allowed: {:#}", err ), } Err(err) } else { debug!("Successfully tested for user namespaces"); Ok(()) } } #[cfg(not(target_os = "linux"))] pub async fn test_for_unprivileged_userns_clone() -> Result<()> { Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_image_ref() -> Result<()> { let image_ref = ImageRef::from_str("rust")?; assert_eq!( image_ref, ImageRef { repo: "rust".to_string(), tag: None, digest: None, } ); Ok(()) } #[test] fn test_parse_image_ref_digest() -> Result<()> { let image_ref = ImageRef::from_str( "rust@sha256:28ee8822965a932e229599b59928f8c2655b2a198af30568acf63e8aff0e8a3a", )?; assert_eq!( image_ref, ImageRef { repo: "rust".to_string(), tag: None, digest: Some( "sha256:28ee8822965a932e229599b59928f8c2655b2a198af30568acf63e8aff0e8a3a" .to_string() ), } ); Ok(()) } #[test] fn test_parse_image_ref_tag() -> Result<()> { let image_ref = ImageRef::from_str("rust:1-alpine3.18")?; assert_eq!( image_ref, ImageRef { repo: "rust".to_string(), tag: Some("1-alpine3.18".to_string()), digest: None, } ); Ok(()) } } repro-env-0.4.1/src/errors.rs000064400000000000000000000001551046102023000142150ustar 00000000000000pub use anyhow::{anyhow, bail, Context as _, Error, Result}; pub use log::{debug, error, info, trace, warn}; repro-env-0.4.1/src/fetch.rs000064400000000000000000000131001046102023000137640ustar 00000000000000use crate::args; use crate::container; use crate::errors::*; use crate::http; use crate::lockfile::{Lockfile, PackageLock}; use crate::paths; use crate::pkgs; use sha2::{Digest, Sha256}; use std::path::Path; use tokio::fs; use tokio::io::{AsyncSeekExt, AsyncWriteExt}; pub async fn download_dependencies(dependencies: &[PackageLock]) -> Result<()> { let client = http::Client::new()?; let pkgs_cache_dir = paths::pkgs_cache_dir()?; for package in dependencies { trace!("Found dependencies: {package:?}"); let path = pkgs_cache_dir.sha256_path(&package.sha256)?; if path.exists() { debug!( "Package already in cache: {:?} {:?}", package.name, package.version ); } else { let parent = path .parent() .context("Failed to determine parent directory")?; fs::create_dir_all(parent).await.with_context(|| { anyhow!("Failed to create parent directories for file: {path:?}") })?; let mut dl_path = path.clone(); dl_path.as_mut_os_string().push(".tmp"); let file = fs::OpenOptions::new() .write(true) .create(true) .truncate(false) .open(&dl_path) .await?; let mut lock = fd_lock::RwLock::new(file); debug!("Trying to acquire write lock for file: {path:?}"); let mut lock = lock .write() .with_context(|| anyhow!("Failed to acquire lock for {dl_path:?}"))?; // check if file became available in meantime if path.exists() { debug!("File became available in the meantime, nothing to do"); } else { debug!( "Downloading package into cache: {:?} {:?}", package.name, package.version ); lock.set_len(0).await.context("Failed to truncate file")?; lock.rewind() .await .context("Failed to rewind file to beginning")?; let mut response = client.request(&package.url).await.with_context(|| { anyhow!("Failed to download package from url: {:?}", package.url) })?; let mut hasher = Sha256::new(); while let Some(chunk) = response .chunk() .await .context("Failed to read from download stream")? { lock.write_all(&chunk) .await .context("Failed to write to downloaded data to disk")?; hasher.update(&chunk); } let result = hex::encode(hasher.finalize()); if package.sha256 != result { lock.set_len(0) .await .context("Mismatch of sha256, failed to truncate file")?; bail!( "Mismatch of sha256, expected={:?}, downloaded={:?}", package.sha256, result ); } lock.sync_all() .await .context("Failed to sync downloaded data to disk")?; fs::rename(&dl_path, &path) .await .with_context(|| anyhow!("Failed to rename {dl_path:?} to {path:?}"))?; } } } Ok(()) } pub fn verify_pin_metadata(pkg: &[u8], pin: &PackageLock) -> Result<()> { let pkg = match pin.system.as_str() { "alpine" => pkgs::alpine::parse(pkg).context("Failed to parse data as alpine package")?, "archlinux" => { pkgs::archlinux::parse(pkg).context("Failed to parse data as archlinux package")? } "debian" => pkgs::debian::parse(pkg).context("Failed to parse data as debian package")?, system => bail!("Unknown package system: {system:?}"), }; debug!("Parsed embedded metadata from package: {pkg:?}"); if pin.name != pkg.name { bail!( "Package name in metadata doesn't match lockfile: expected={:?}, embedded={:?}", pin.name, pkg.name ); } if pin.version != pkg.version { bail!( "Package version in metadata doesn't match lockfile: expected={:?}, embedded={:?}", pin.version, pkg.version ); } Ok(()) } pub async fn fetch(fetch: &args::Fetch) -> Result<()> { // load lockfile let path = fetch.file.as_deref().unwrap_or(Path::new("repro-env.lock")); let buf = fs::read_to_string(path) .await .with_context(|| anyhow!("Failed to read dependency lockfile: {path:?}"))?; let lockfile = Lockfile::deserialize(&buf)?; trace!("Loaded dependency lockfile from file: {lockfile:?}"); if !fetch.no_pull { let image = &lockfile.container.image; if let Err(err) = container::inspect(image).await { debug!("Could not find image in cache: {err:#}"); container::pull(image).await?; } else { info!("Found container image in local cache: {image:?}"); } } // ignore packages that are already present in the container let dependencies = lockfile .packages .into_iter() .filter(|p| !p.installed) .collect::>(); if !dependencies.is_empty() { download_dependencies(&dependencies).await?; } Ok(()) } repro-env-0.4.1/src/http.rs000064400000000000000000000017021046102023000136570ustar 00000000000000use crate::errors::*; static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); pub struct Client { http: reqwest::Client, } impl Client { pub fn new() -> Result { let http = reqwest::Client::builder() .user_agent(APP_USER_AGENT) .build()?; Ok(Client { http }) } pub async fn request(&self, url: &str) -> Result { info!("Downloading {url:?}..."); let response = self .http .get(url) .send() .await .context("Failed to send http request")? .error_for_status() .context("Received http error")?; Ok(response) } pub async fn fetch(&self, url: &str) -> Result { let response = self.request(url).await?; let buf = response.bytes().await.context("Failed to read http body")?; Ok(buf) } } repro-env-0.4.1/src/lib.rs000064400000000000000000000003741046102023000134520ustar 00000000000000pub mod args; pub mod build; pub mod container; pub mod errors; pub mod fetch; pub mod http; pub mod lockfile; pub mod manifest; pub mod paths; pub mod pgp; pub mod pkgs; pub mod resolver; #[cfg(test)] pub mod test_data; pub mod update; pub mod utils; repro-env-0.4.1/src/lockfile.rs000064400000000000000000000167241046102023000145020ustar 00000000000000use crate::errors::*; use serde::{Deserialize, Serialize}; use std::path::Path; use tokio::fs; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Lockfile { pub container: ContainerLock, #[serde(default, rename = "package", skip_serializing_if = "Vec::is_empty")] pub packages: Vec, } impl Lockfile { pub fn deserialize(buf: &str) -> Result { let lockfile = toml::from_str(buf)?; Ok(lockfile) } pub fn serialize(&self) -> Result { let toml = toml::to_string_pretty(self)?; Ok(toml) } pub async fn read_from_file>(path: P) -> Result { let path = path.as_ref(); let buf = fs::read_to_string(&path) .await .with_context(|| anyhow!("Failed to read dependency lockfile: {path:?}"))?; let lockfile = Self::deserialize(&buf)?; trace!("Loaded dependency lockfile from file: {lockfile:?}"); Ok(lockfile) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ContainerLock { pub image: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PackageLock { pub name: String, pub version: String, pub system: String, pub url: String, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub provides: Vec, pub sha256: String, #[serde(skip_serializing_if = "Option::is_none")] pub signature: Option, /// If true, this package is already present in the container and does not /// need to be installed. It's only in the lockfile to make the /// repro-env.lock diff easier to read and help git's delta-compression. #[serde(default, skip_serializing_if = "is_false")] pub installed: bool, } fn is_false(value: &bool) -> bool { !value } #[cfg(test)] mod tests { use super::*; #[test] pub fn test_serialize_archlinux() -> Result<()> { let lockfile = Lockfile { container: ContainerLock { image: "docker.io/library/archlinux@sha256:6568d3f1f278827a4a7d8537f80c2ae36982829a0c6bccff4cec081774025472" .to_string(), }, packages: vec![ PackageLock { name: "archlinux-keyring".to_string(), version: "20230704-1".to_string(), system: "archlinux".to_string(), url: "https://archive.archlinux.org/packages/a/archlinux-keyring/archlinux-keyring-20230704-1-any.pkg.tar.zst".to_string(), provides: vec![], sha256: "6a3d2acaa396c4bd72fe3f61a3256d881e3fc2cf326113cf331f168e36dd9a3c".to_string(), signature: Some( "iHUEABYIAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZKPPXgAKCRBtQr3RFuAGj9oXAP94RQ1sKD53/RxVYlVEEOjKHvOmrWvDkt1veMYygnlnIgD+MLg/TT6d71kE8F08+JH+EcnG7wQow5Xr/qBo1VPLdgQ=".to_string()), installed: false, }, PackageLock { name: "binutils".to_string(), version: "2.40-6".to_string(), system: "archlinux".to_string(), url: "https://archive.archlinux.org/packages/b/binutils/binutils-2.40-6-x86_64.pkg.tar.zst".to_string(), provides: vec![], sha256: "b65fd16001578e10b602e577a8031cbfffc1164caf47ed9ba00c60d804519430".to_string(), signature: Some( "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZG6Rg18UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCge2AQD/LGBeHRaeO8xh4E/bAYfqd1O/OFqk2DrQBJ73cdKl2gD9EC8p4U/cXQK8V774m6LSS50usH5pxcQWEq/H0SF+FgM=".to_string()), installed: false, } ], }; let toml = lockfile.serialize()?; assert_eq!( toml, r#"[container] image = "docker.io/library/archlinux@sha256:6568d3f1f278827a4a7d8537f80c2ae36982829a0c6bccff4cec081774025472" [[package]] name = "archlinux-keyring" version = "20230704-1" system = "archlinux" url = "https://archive.archlinux.org/packages/a/archlinux-keyring/archlinux-keyring-20230704-1-any.pkg.tar.zst" sha256 = "6a3d2acaa396c4bd72fe3f61a3256d881e3fc2cf326113cf331f168e36dd9a3c" signature = "iHUEABYIAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZKPPXgAKCRBtQr3RFuAGj9oXAP94RQ1sKD53/RxVYlVEEOjKHvOmrWvDkt1veMYygnlnIgD+MLg/TT6d71kE8F08+JH+EcnG7wQow5Xr/qBo1VPLdgQ=" [[package]] name = "binutils" version = "2.40-6" system = "archlinux" url = "https://archive.archlinux.org/packages/b/binutils/binutils-2.40-6-x86_64.pkg.tar.zst" sha256 = "b65fd16001578e10b602e577a8031cbfffc1164caf47ed9ba00c60d804519430" signature = "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZG6Rg18UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCge2AQD/LGBeHRaeO8xh4E/bAYfqd1O/OFqk2DrQBJ73cdKl2gD9EC8p4U/cXQK8V774m6LSS50usH5pxcQWEq/H0SF+FgM=" "# ); let deserialized = Lockfile::deserialize(&toml)?; assert_eq!(deserialized, lockfile); Ok(()) } #[test] pub fn test_serialize_debian() -> Result<()> { let lockfile = Lockfile { container: ContainerLock { image: "debian@sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b" .to_string(), }, packages: vec![ PackageLock { name: "binutils".to_string(), version: "2.40-2".to_string(), system: "debian".to_string(), url: "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils_2.40-2_amd64.deb".to_string(), provides: vec![], sha256: "83c3e20b53e1fbd84d764c3ba27d26a0376e361ae5d7fb37120196934dd87424".to_string(), signature: None, installed: false, }, PackageLock { name: "binutils-common".to_string(), version: "2.40-2".to_string(), system: "debian".to_string(), url: "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils-common_2.40-2_amd64.deb".to_string(), provides: vec![], sha256: "ab314134f43a0891a48f69a9bc33d825da748fa5e0ba2bebb7a5c491b026f1a0".to_string(), signature: None, installed: false, } ], }; let toml = lockfile.serialize()?; assert_eq!( toml, r#"[container] image = "debian@sha256:3d868b5eb908155f3784317b3dda2941df87bbbbaa4608f84881de66d9bb297b" [[package]] name = "binutils" version = "2.40-2" system = "debian" url = "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils_2.40-2_amd64.deb" sha256 = "83c3e20b53e1fbd84d764c3ba27d26a0376e361ae5d7fb37120196934dd87424" [[package]] name = "binutils-common" version = "2.40-2" system = "debian" url = "https://snapshot.debian.org/archive/debian/20230115T211934Z/pool/main/b/binutils/binutils-common_2.40-2_amd64.deb" sha256 = "ab314134f43a0891a48f69a9bc33d825da748fa5e0ba2bebb7a5c491b026f1a0" "# ); let deserialized = Lockfile::deserialize(&toml)?; assert_eq!(deserialized, lockfile); Ok(()) } } repro-env-0.4.1/src/main.rs000064400000000000000000000017631046102023000136330ustar 00000000000000use clap::Parser; use env_logger::Env; use repro_env::args::{Args, SubCommand}; use repro_env::build; use repro_env::errors::*; use repro_env::fetch; use repro_env::update; use std::env; use std::io; #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); let log_level = match args.verbose { 0 => "info", 1 => "debug", _ => "trace", }; env_logger::init_from_env(Env::default().default_filter_or(log_level)); if let Some(path) = args.context { debug!("Changing current directory to {path:?}..."); env::set_current_dir(&path) .with_context(|| anyhow!("Failed to switch to directory {path:?}"))?; } match args.subcommand { SubCommand::Build(build) => build::build(&build).await, SubCommand::Update(update) => update::update(&update).await, SubCommand::Fetch(fetch) => fetch::fetch(&fetch).await, SubCommand::Completions(completions) => completions.generate(io::stdout()), } } repro-env-0.4.1/src/manifest.rs000064400000000000000000000045771046102023000145230ustar 00000000000000use crate::errors::*; use crate::lockfile::Lockfile; use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::path::Path; use tokio::fs; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Manifest { pub container: ContainerManifest, pub packages: Option, } impl Manifest { pub fn deserialize(buf: &str) -> Result { let manifest = toml::from_str(buf).context("Failed to load manifest from toml")?; Ok(manifest) } pub async fn read_from_file>(path: P) -> Result { let path = path.as_ref(); let buf = fs::read_to_string(&path) .await .with_context(|| anyhow!("Failed to read dependency manifest: {path:?}"))?; let manifest = Self::deserialize(&buf)?; debug!("Loaded manifest from file: {manifest:?}"); Ok(manifest) } pub fn satisfied_by(&self, lockfile: &Lockfile) -> Result<()> { if let Some(packages) = &self.packages { let mut provided = HashSet::new(); for package in &lockfile.packages { provided.insert(package.name.clone()); provided.extend(package.provides.iter().cloned()); } for dependency in &packages.dependencies { let (name, _) = dependency.split_once('=').unwrap_or((dependency, "")); if !provided.contains(name) { bail!("Lockfile does not satisify dependency: {dependency:?}"); } } } Ok(()) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ContainerManifest { pub image: String, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PackagesManifest { pub system: String, #[serde(default)] pub dependencies: IndexSet, } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_manifest() -> Result<()> { let manifest = Manifest::deserialize( r#"[container] image = "docker.io/library/rust:1-alpine" "#, )?; assert_eq!( manifest, Manifest { container: ContainerManifest { image: "docker.io/library/rust:1-alpine".to_string(), }, packages: None } ); Ok(()) } } repro-env-0.4.1/src/paths.rs000064400000000000000000000126671046102023000140330ustar 00000000000000use crate::errors::*; use std::env; use std::io::ErrorKind; use std::path::{Component, Path, PathBuf}; use tokio::fs; static SHARD_SIZE: usize = 2; pub fn repro_env_dir() -> Result { if let Some(path) = env::var_os("REPRO_ENV_HOME") { Ok(path.into()) } else { let mut cache = dirs::cache_dir().context("Failed to detect cache directory")?; cache.push("repro-env"); Ok(cache) } } pub fn cache_dir() -> Result { if let Some(path) = env::var_os("REPRO_ENV_CACHE") { Ok(path.into()) } else { repro_env_dir() } } pub fn pkgs_cache_dir() -> Result { let mut path = cache_dir()?; path.push("pkgs"); Ok(PkgsCacheDir { path }) } pub fn alpine_cache_dir() -> Result { let mut path = cache_dir()?; path.push("alpine"); Ok(PkgsCacheDir { path }) } #[derive(Debug)] pub struct PkgsCacheDir { path: PathBuf, } impl PkgsCacheDir { fn shard<'a>(hash: &'a str, algo: &'static str, len: usize) -> Result<(&'a str, &'a str)> { if hash.len() != len { bail!("Unexpected {algo} checksum length: {:?}", hash.len()); } if !hash.chars().all(char::is_alphanumeric) { bail!("Unexpected characters in {algo}: {hash:?}"); } let shard = &hash[..SHARD_SIZE]; let suffix = &hash[SHARD_SIZE..]; Ok((shard, suffix)) } fn shard_sha256(sha256: &str) -> Result<(&str, &str)> { Self::shard(sha256, "sha256", 64) } fn shard_sha1(sha1: &str) -> Result<(&str, &str)> { Self::shard(sha1, "sha1", 40) } pub fn sha256_path(&self, sha256: &str) -> Result { let (shard, suffix) = Self::shard_sha256(sha256)?; let mut path = self.path.clone(); path.push(shard); path.push(suffix); Ok(path) } fn sha1_path(&self, sha1: &str) -> Result { let (shard, suffix) = Self::shard_sha1(sha1)?; let mut path = self.path.clone(); path.push(shard); path.push(suffix); Ok(path) } pub async fn sha1_read_link(&self, sha1: &str) -> Result> { let path = self.sha1_path(sha1)?; match fs::read_link(&path).await { Ok(path) => { trace!("Found symlink in cache: {path:?}"); let sha256 = Self::link_to_sha256(&path)?; Ok(Some(sha256)) } Err(err) if err.kind() == ErrorKind::NotFound => { trace!("Did not find symlink in cache: {path:?}"); Ok(None) } Err(err) => Err(err.into()), } } pub fn sha1_to_sha256(&self, sha1: &str, sha256: &str) -> Result<(PathBuf, PathBuf)> { let sha1_path = self.sha1_path(sha1)?; let (shard, suffix) = Self::shard_sha256(sha256)?; let mut sha256_path = PathBuf::from("../../pkgs"); sha256_path.push(shard); sha256_path.push(suffix); Ok((sha1_path, sha256_path)) } fn link_to_sha256(path: &Path) -> Result { let mut components = path.components().rev(); let tail = components.next().context("Link is missing filename")?; let shard = components.next().context("Link is missing shard")?; let tail = Self::component_to_name(&tail)?; let shard = Self::component_to_name(&shard)?; Ok(format!("{shard}{tail}")) } fn component_to_name<'a>(comp: &'a Component) -> Result<&'a str> { let Component::Normal(comp) = comp else { bail!("Component has reserved name") }; let Some(comp) = comp.to_str() else { bail!("Component is invalid utf8") }; Ok(comp) } } #[cfg(test)] mod tests { use super::*; use std::path::Path; #[test] fn test_sha256_path() { let dir = PkgsCacheDir { path: PathBuf::from("/cache"), }; assert!(dir.sha256_path("").is_err()); assert!(dir.sha256_path("ffff").is_err()); assert!(dir .sha256_path("////////////////////////////////////////////////////////////////") .is_err()); let path = dir .sha256_path("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") .unwrap(); assert_eq!( path, Path::new("/cache/ff/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") ); } #[test] fn test_sha1_read_link() -> Result<()> { let path = PkgsCacheDir::link_to_sha256(Path::new( "../../../pkgs/ff/7951b5950a3a0319e86988041db4438b31a6ee4c7a36c64bd6c0c4607e40c9", ))?; assert_eq!( path, "ff7951b5950a3a0319e86988041db4438b31a6ee4c7a36c64bd6c0c4607e40c9" ); Ok(()) } #[test] fn test_sha1_to_sha256() -> Result<()> { let dir = PkgsCacheDir { path: PathBuf::from("/cache"), }; let (sha1, sha256) = dir.sha1_to_sha256( "83d8ab27f4fd4725a147245f89d076aa96b52262", "ff7951b5950a3a0319e86988041db4438b31a6ee4c7a36c64bd6c0c4607e40c9", )?; assert_eq!( sha1, Path::new("/cache/83/d8ab27f4fd4725a147245f89d076aa96b52262") ); assert_eq!( sha256, Path::new( "../../pkgs/ff/7951b5950a3a0319e86988041db4438b31a6ee4c7a36c64bd6c0c4607e40c9" ) ); Ok(()) } } repro-env-0.4.1/src/pgp.rs000064400000000000000000000100331046102023000134630ustar 00000000000000use crate::errors::*; use crate::lockfile::PackageLock; use data_encoding::BASE64; use sequoia_openpgp::parse::{PacketParser, PacketParserResult, Parse}; use sequoia_openpgp::Packet; use std::cmp; use std::time; use std::time::SystemTime; pub fn parse_timestamp_from_sig(buf: &[u8]) -> Result> { let mut ppr = PacketParser::from_bytes(buf)?; while let PacketParserResult::Some(pp) = ppr { let (packet, next_ppr) = pp.recurse()?; ppr = next_ppr; debug!("Found packet in pgp data: {packet:?}"); let Packet::Signature(sig) = &packet else { continue; }; let Some(time) = sig.signature_creation_time() else { continue; }; return Ok(Some(time)); } Ok(None) } pub fn find_max_signature_time<'a, I: Iterator>( pkgs: I, ) -> Result> { let mut current_max = None; for pkg in pkgs { let base64 = pkg .signature .as_ref() .context("Package in dependency lockfile is missing signature")?; let signature = BASE64 .decode(base64.as_bytes()) .with_context(|| anyhow!("Failed to decode signature as base64: {base64:?}"))?; match (parse_timestamp_from_sig(&signature), &mut current_max) { (Ok(Some(time)), Some(max)) => { *max = cmp::max(*max, time); } (Ok(Some(time)), max) => *max = Some(time), (Ok(None), _) => (), (Err(err), _) => { warn!("Failed to parse timestamp from signature {base64:?}: {err:#?}") } } } Ok(current_max) } #[cfg(test)] mod tests { use super::*; use data_encoding::BASE64; #[test] fn test_parse_sig() { let buf = BASE64.decode(b"iHUEABYKAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZcU7FAAKCRBtQr3RFuAGj4Y4AQCKsihdyJWyNGBwQ9Kd5AmenehuvR4xfFOCjIOndQCYhwD+NFzEjbwraHHVtEjQh4HtrnZPc0JplQvM5zRT3gDCawE=").unwrap(); let time = parse_timestamp_from_sig(&buf).unwrap().unwrap(); let expected = time::UNIX_EPOCH .checked_add(time::Duration::from_secs(1707424532)) .unwrap(); assert_eq!(time, expected); } #[test] fn test_max_signature_time() { let pkgs = [ PackageLock { name: "archlinux-keyring".to_string(), version: "20230704-1".to_string(), system: "archlinux".to_string(), url: "https://archive.archlinux.org/packages/a/archlinux-keyring/archlinux-keyring-20230704-1-any.pkg.tar.zst".to_string(), provides: vec![], sha256: "6a3d2acaa396c4bd72fe3f61a3256d881e3fc2cf326113cf331f168e36dd9a3c".to_string(), signature: Some( "iHUEABYIAB0WIQQEKYl95fO9rFN6MGltQr3RFuAGjwUCZKPPXgAKCRBtQr3RFuAGj9oXAP94RQ1sKD53/RxVYlVEEOjKHvOmrWvDkt1veMYygnlnIgD+MLg/TT6d71kE8F08+JH+EcnG7wQow5Xr/qBo1VPLdgQ=".to_string()), installed: false, }, PackageLock { name: "binutils".to_string(), version: "2.40-6".to_string(), system: "archlinux".to_string(), url: "https://archive.archlinux.org/packages/b/binutils/binutils-2.40-6-x86_64.pkg.tar.zst".to_string(), provides: vec![], sha256: "b65fd16001578e10b602e577a8031cbfffc1164caf47ed9ba00c60d804519430".to_string(), signature: Some( "iNUEABYKAH0WIQQFx3danouXdAf+COadTFqhVCbaCgUCZG6Rg18UgAAAAAAuAChpc3N1ZXItZnByQG5vdGF0aW9ucy5vcGVucGdwLmZpZnRoaG9yc2VtYW4ubmV0MDVDNzc3NUE5RThCOTc3NDA3RkUwOEU2OUQ0QzVBQTE1NDI2REEwQQAKCRCdTFqhVCbaCge2AQD/LGBeHRaeO8xh4E/bAYfqd1O/OFqk2DrQBJ73cdKl2gD9EC8p4U/cXQK8V774m6LSS50usH5pxcQWEq/H0SF+FgM=".to_string()), installed: false, } ]; let time = find_max_signature_time(pkgs.iter()).unwrap(); let expected = time::UNIX_EPOCH .checked_add(time::Duration::from_secs(1688457054)) .unwrap(); assert_eq!(time, Some(expected)); } } repro-env-0.4.1/src/pkgs/alpine.rs000064400000000000000000000051131046102023000151140ustar 00000000000000use crate::errors::*; use crate::pkgs::Pkg; use crate::utils; use flate2::read::GzDecoder; use std::io::{BufRead, BufReader, Read}; pub fn parse_pkginfo(reader: R) -> Result { let reader = BufReader::new(reader); let mut name = None; let mut version = None; for line in reader.lines() { let line = line?; if let Some(value) = line.strip_prefix("pkgname = ") { name = Some(value.to_string()); } else if let Some(value) = line.strip_prefix("pkgver = ") { version = Some(value.to_string()); } } Ok(Pkg { name: name.context("Could not find pkgname in .PKGINFO")?, version: version.context("Could not find pkgver in .PKGINFO")?, }) } pub fn parse(reader: R) -> Result { let mut r = BufReader::new(reader); utils::read_gzip_to_end(&mut r).context("Failed to strip signature")?; let gz = GzDecoder::new(r); let mut tar = tar::Archive::new(gz); for entry in tar.entries()? { let entry = entry?; let path = entry.path()?; if path.to_str() == Some(".PKGINFO") { return parse_pkginfo(entry); } } bail!("Failed to find .PKGINFO in package file") } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_pkginfo() -> Result<()> { let data = br#"# Generated by abuild 3.11.1-r0 # using fakeroot version 1.31 # Thu Jul 20 10:08:19 UTC 2023 pkgname = mpfr4 pkgver = 4.2.0_p12-r0 pkgdesc = multiple-precision floating-point library url = https://www.mpfr.org/ builddate = 1689847699 packager = Buildozer size = 684032 arch = x86_64 origin = mpfr4 commit = 8910287059187d6e83a13dd2078a2a42ea2121f4 maintainer = Natanael Copa license = LGPL-3.0-or-later replaces = mpfr provides = mpfr=4.2.0_p12-r0 # automatically detected: provides = so:libmpfr.so.6=6.2.0 depend = so:libc.musl-x86_64.so.1 depend = so:libgmp.so.10 datahash = a2c44c6b313ca65980d7f610026a71e6119d119de6cf2b78e52464d9d80bff45 "#; let pkg = parse_pkginfo(&data[..])?; assert_eq!( pkg, Pkg { name: "mpfr4".to_string(), version: "4.2.0_p12-r0".to_string(), } ); Ok(()) } #[test] fn test_parse_pkg() -> Result<()> { let pkg = parse(crate::test_data::ALPINE_APK_EXAMPLE)?; assert_eq!( pkg, Pkg { name: "alpine-base".to_string(), version: "3.18.3-r0".to_string(), } ); Ok(()) } } repro-env-0.4.1/src/pkgs/archlinux.rs000064400000000000000000000117131046102023000156440ustar 00000000000000use crate::container::Container; use crate::errors::*; use crate::pkgs::Pkg; use peekread::{BufPeekReader, PeekRead}; use std::fmt::Write; use std::io::{BufRead, BufReader, Read}; use std::time::SystemTime; use std::time::UNIX_EPOCH; pub const GPG_CONF_DIR: &str = "/etc/pacman.d/gnupg/"; pub const GPG_CONF_FILENAME: &str = "gpg.conf"; pub enum Compression { Xz, Zstd, None, } pub fn detect_compression(mut reader: R) -> Result { let mut buf = [0u8; 6]; reader .read_exact(&mut buf) .context("Failed to read magic bytes from archive")?; if buf.starts_with(&[0x28, 0xB5, 0x2F, 0xFD]) { Ok(Compression::Zstd) } else if buf.starts_with(&[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]) { Ok(Compression::Xz) } else { Ok(Compression::None) } } pub fn parse_pkginfo(reader: R) -> Result { let reader = BufReader::new(reader); let mut name = None; let mut version = None; for line in reader.lines() { let line = line?; if let Some(value) = line.strip_prefix("pkgname = ") { name = Some(value.to_string()); } else if let Some(value) = line.strip_prefix("pkgver = ") { version = Some(value.to_string()); } } Ok(Pkg { name: name.context("Could not find pkgname in .PKGINFO")?, version: version.context("Could not find pkgver in .PKGINFO")?, }) } pub fn parse_tar(reader: R) -> Result { let mut tar = tar::Archive::new(reader); for entry in tar.entries()? { let entry = entry?; let path = entry.path()?; if path.to_str() == Some(".PKGINFO") { return parse_pkginfo(entry); } } bail!("Failed to find .PKGINFO in package file") } pub fn parse(reader: R) -> Result { let mut reader = BufPeekReader::new(reader); match detect_compression(reader.peek())? { Compression::Xz => { let mut buf = Vec::new(); lzma_rs::xz_decompress(&mut reader, &mut buf)?; parse_tar(&buf[..]) } Compression::Zstd => { let decoder = ruzstd::StreamingDecoder::new(reader)?; parse_tar(decoder) } Compression::None => parse_tar(reader), } } pub async fn set_pacman_verification_datetime( container: &Container, time: SystemTime, ) -> Result<()> { let path = format!("{GPG_CONF_DIR}{GPG_CONF_FILENAME}"); let gpg_conf = container.cat(&path).await?; let mut gpg_conf = String::from_utf8(gpg_conf).context("Failed to parse gpg.conf as utf-8")?; if !gpg_conf.ends_with('\n') { gpg_conf.push('\n'); } // check if a faked-system-time is already configured if let Some(line) = gpg_conf .lines() .find(|line| line.starts_with("faked-system-time")) { warn!("Container already defines a verification datetime: {line:?}"); return Ok(()); } let epoch = time .duration_since(UNIX_EPOCH) .with_context(|| anyhow!("Failed to derive unix epoch from time {time:?}"))?; writeln!(gpg_conf, "faked-system-time {}", epoch.as_secs())?; container .write_file(GPG_CONF_DIR, GPG_CONF_FILENAME, gpg_conf.as_bytes()) .await?; Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_pkg() -> Result<()> { let archive = { let data = br#"# Generated by makepkg 6.0.2 # using fakeroot version 1.31 pkgname = gcc pkgbase = gcc pkgver = 13.1.1-1 pkgdesc = The GNU Compiler Collection - C and C++ frontends url = https://gcc.gnu.org builddate = 1682849478 packager = Frederik Schwan size = 190564290 arch = x86_64 license = GPL3 license = LGPL license = FDL license = custom replaces = gcc-multilib provides = gcc-multilib depend = gcc-libs=13.1.1-1 depend = binutils>=2.28 depend = libmpc depend = zstd depend = libisl.so=23-64 optdepend = lib32-gcc-libs: for generating code for 32-bit ABI makedepend = binutils makedepend = doxygen makedepend = gcc-ada makedepend = gcc-d makedepend = git makedepend = lib32-glibc makedepend = lib32-gcc-libs makedepend = libisl makedepend = libmpc makedepend = python makedepend = zstd checkdepend = dejagnu checkdepend = expect checkdepend = inetutils checkdepend = python-pytest checkdepend = tcl "#; let mut tar = tar::Builder::new(Vec::new()); let mut header = tar::Header::new_gnu(); header.set_path(".PKGINFO")?; header.set_size(data.len() as u64); header.set_cksum(); tar.append(&header, &data[..])?; tar.into_inner()? }; let mut buf = Vec::new(); lzma_rs::xz_compress(&mut &archive[..], &mut buf)?; let pkg = parse(&buf[..]).context("Failed to parse package")?; assert_eq!( pkg, Pkg { name: "gcc".to_string(), version: "13.1.1-1".to_string(), } ); Ok(()) } } repro-env-0.4.1/src/pkgs/debian.rs000064400000000000000000000206621046102023000150740ustar 00000000000000use crate::errors::*; use crate::pkgs::Pkg; use std::io::BufReader; use std::io::Read; pub fn parse_control(control: &str) -> Result { let mut name = None; let mut version = None; for line in control.lines() { if let Some(value) = line.strip_prefix("Package: ") { name = Some(value.to_string()); } if let Some(value) = line.strip_prefix("Version: ") { version = Some(value.to_string()); } } Ok(Pkg { name: name.context("Failed to find package name in deb control data")?, version: version.context("Failed to find package version in deb control data")?, }) } pub fn parse_control_tar(filename: &[u8], reader: R) -> Result { let mut buf = Vec::new(); let mut reader = BufReader::new(reader); match filename { b"control.tar.xz" => lzma_rs::xz_decompress(&mut reader, &mut buf)?, _ => bail!("Unsupported compression for control.tar: {filename:?}"), } let mut tar = tar::Archive::new(&buf[..]); for entry in tar.entries()? { let mut entry = entry?; let path = entry.path()?; let filename = path .to_str() .with_context(|| anyhow!("Package contains paths with invalid encoding: {:?}", path))?; if filename == "./control" { let mut buf = String::new(); entry.read_to_string(&mut buf)?; return parse_control(&buf); } } bail!("Failed to find control data in control.tar") } pub fn parse(reader: R) -> Result { let mut archive = ar::Archive::new(reader); while let Some(entry) = archive.next_entry() { let mut entry = entry?; let filename = entry.header().identifier(); if !filename.starts_with(b"control.tar") { continue; } let filename = filename.to_owned(); return parse_control_tar(&filename, &mut entry); } bail!("Failed to find control data") } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_control_data() -> Result<()> { let data = "Package: binutils-common\nSource: binutils\nVersion: 2.40-2\nArchitecture: amd64\nMaintainer: Matthias Klose \nInstalled-Size: 15021\nBreaks: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nReplaces: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nSection: devel\nPriority: optional\nMulti-Arch: same\nHomepage: https://www.gnu.org/software/binutils/\nDescription: Common files for the GNU assembler, linker and binary utilities\n This package contains the localization files used by binutils packages for\n various target architectures and parts of the binutils documentation. It is\n not useful on its own.\n"; let data = parse_control(data)?; assert_eq!( data, Pkg { name: "binutils-common".to_string(), version: "2.40-2".to_string(), } ); Ok(()) } #[test] fn test_parse_deb() -> Result<()> { let tar = { let data = b"Package: binutils-common\nSource: binutils\nVersion: 2.40-2\nArchitecture: amd64\nMaintainer: Matthias Klose \nInstalled-Size: 15021\nBreaks: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nReplaces: binutils (<< 2.38.50.20220527-2), binutils-multiarch (<< 2.38.50.20220527-2)\nSection: devel\nPriority: optional\nMulti-Arch: same\nHomepage: https://www.gnu.org/software/binutils/\nDescription: Common files for the GNU assembler, linker and binary utilities\n This package contains the localization files used by binutils packages for\n various target architectures and parts of the binutils documentation. It is\n not useful on its own.\n"; let mut tar = tar::Builder::new(Vec::new()); // it's non-trivial to make a tar::Header with path set to `./control`, so we parse an existing one let mut header = tar::Header::from_byte_slice(&[ 0x2e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x36, 0x34, 0x34, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x37, 0x30, 0x34, 0x00, 0x31, 0x34, 0x34, 0x31, 0x34, 0x37, 0x34, 0x35, 0x31, 0x30, 0x34, 0x00, 0x30, 0x31, 0x31, 0x33, 0x32, 0x32, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]) .clone(); header.set_size(data.len() as u64); header.set_cksum(); tar.append(&header, &data[..])?; tar.into_inner()? }; let compressed = { let mut compressed = Vec::new(); lzma_rs::xz_compress(&mut &tar[..], &mut compressed)?; compressed }; let deb = { let mut ar = ar::Builder::new(Vec::new()); let header = ar::Header::new(b"control.tar.xz".to_vec(), compressed.len() as u64); ar.append(&header, &compressed[..])?; ar.into_inner()? }; let pkg = parse(&deb[..])?; assert_eq!( pkg, Pkg { name: "binutils-common".to_string(), version: "2.40-2".to_string(), } ); Ok(()) } } repro-env-0.4.1/src/pkgs/mod.rs000064400000000000000000000002221046102023000144170ustar 00000000000000pub mod alpine; pub mod archlinux; pub mod debian; #[derive(Debug, PartialEq)] pub struct Pkg { pub name: String, pub version: String, } repro-env-0.4.1/src/resolver/alpine.rs000064400000000000000000000310221046102023000160070ustar 00000000000000use crate::args; use crate::container::{self, Container}; use crate::errors::*; use crate::http; use crate::lockfile::{ContainerLock, PackageLock}; use crate::manifest::PackagesManifest; use crate::paths; use crate::utils; use data_encoding::BASE64; use flate2::bufread::GzDecoder; use sha1::Sha1; use sha2::{Digest, Sha256}; use std::collections::{HashMap, HashSet}; use std::io::{BufRead, BufReader, Read}; use std::rc::Rc; use tokio::fs; pub fn decode_apk_checksum(checksum: &str) -> Result> { let checksum = checksum .strip_prefix("Q1") .with_context(|| anyhow!("Only checksums starting with Q1 are supported: {checksum:?}"))?; let checksum = BASE64 .decode(checksum.as_bytes()) .context("Failed to decode checksum as base64")?; Ok(checksum) } #[derive(Debug, Default)] pub struct DatabaseCache { repos: HashMap>, pkgs: HashMap, } #[derive(Debug)] pub struct CacheEntry { name: String, version: String, arch: String, provides: Vec, checksum: String, repo_url: Rc, } pub struct CacheEntryDraft { pub name: Option, pub version: Option, pub arch: Option, pub provides: Vec, pub checksum: Option, pub repo_url: Rc, } impl TryFrom for CacheEntry { type Error = Error; fn try_from(draft: CacheEntryDraft) -> Result { Ok(Self { name: draft.name.context("Missing name field")?, version: draft.version.context("Missing version field")?, arch: draft.arch.context("Missing arch field")?, provides: draft.provides, checksum: draft.checksum.context("Missing checksum field")?, repo_url: draft.repo_url, }) } } impl CacheEntryDraft { pub fn new(repo_url: Rc) -> Self { CacheEntryDraft { name: None, version: None, arch: None, provides: vec![], checksum: None, repo_url, } } } impl DatabaseCache { pub fn get(&self, id: &str) -> Result<&CacheEntry> { let entry = self .pkgs .get(id) .context("Failed to find package database entry for: {id:?}")?; Ok(entry) } pub fn read_apkindex_text(&mut self, r: R, repo_url: &Rc) -> Result<()> { let reader = BufReader::new(r); let mut draft = CacheEntryDraft::new(repo_url.clone()); for line in reader.lines() { let line = line?; if line.is_empty() { let mut new = CacheEntryDraft::new(repo_url.clone()); (new, draft) = (draft, new); let pkg = CacheEntry::try_from(new)?; let id = format!("{}-{}", pkg.name, pkg.version); trace!("Inserting pkg into lookup table: {id:?} => {pkg:?}"); self.pkgs.insert(id, pkg); } else if let Some((key, value)) = line.split_once(':') { match key { "P" => { trace!("Package name: {value:?}"); draft.name = Some(value.to_string()); } "V" => { trace!("Package version: {value:?}"); draft.version = Some(value.to_string()); } "C" => { trace!("Package checksum: {value:?}"); let checksum = decode_apk_checksum(value)?; draft.checksum = Some(hex::encode(checksum)); } "A" => { trace!("Package architecture: {value:?}"); draft.arch = Some(value.to_string()); } "p" => { trace!("Package provides: {value:?}"); for entry in value.split(' ') { let (name, _) = entry.split_once('=').unwrap_or((entry, "")); draft.provides.push(name.to_string()); } } _ => trace!("Ignoring APKINDEX value key={key:?}, value={value:?}"), } } else { bail!("Invalid line in index: {line:?}"); } } Ok(()) } pub fn read_apkindex_container(&mut self, r: R, repo_url: &Rc) -> Result<()> { let mut r = BufReader::new(r); utils::read_gzip_to_end(&mut r).context("Failed to strip signature")?; let gz = GzDecoder::new(r); let mut tar = tar::Archive::new(gz); for entry in tar.entries()? { let entry = entry?; if entry.header().entry_type() == tar::EntryType::Regular { let path = entry.path()?; if path.to_str() == Some("APKINDEX") { self.read_apkindex_text(entry, repo_url)?; } } } Ok(()) } pub fn import_from_container(&mut self, buf: &[u8]) -> Result<()> { let mut tar = tar::Archive::new(buf); for entry in tar.entries()? { let entry = entry?; if entry.header().entry_type() == tar::EntryType::Regular { let path = entry.path()?; let file_name = path .file_name() .context("Failed to detect filename")? .to_str() .unwrap_or(""); if let Some(repo_url) = self.repos.get(file_name).cloned() { debug!("Reading package index for repository: {repo_url:?} ({file_name:?})"); self.read_apkindex_container(entry, &repo_url)?; } } } Ok(()) } pub fn register_repo(&mut self, repo: String) { let mut hasher = Sha1::new(); hasher.update(&repo); let hash = hasher.finalize(); let sha1 = hex::encode(&hash[..4]); self.repos .insert(format!("APKINDEX.{sha1}.tar.gz"), Rc::new(repo)); } pub fn init_repos_from_container(&mut self, buf: &[u8]) -> Result<()> { let mut tar = tar::Archive::new(buf); for entry in tar.entries()? { let entry = entry?; if entry.header().entry_type() == tar::EntryType::Regular { let reader = BufReader::new(entry); for repo in reader.lines() { let repo = repo?; debug!("Found repository in /etc/apk/repositories: {repo:?}"); self.register_repo(repo); } } } Ok(()) } } pub fn calculate_checksum_for_apk(apk: &[u8]) -> Result> { // the first gzip has no end-of-stream marker, only read one file from tar let remaining = { let gz = GzDecoder::new(apk); let mut tar = tar::Archive::new(gz); tar.entries()?.next(); tar.into_inner().into_inner() }; // this is slightly chaotic, there's some over-read by GzDecoder that we need to correct let sig = apk.len() - remaining.len() + 8; // locate the start of the 3rd gzip stream let mut r = &apk[sig..]; utils::read_gzip_to_end(&mut r)?; let content = r.len(); // cut at the location of the 2nd gzip stream let control_data = &apk[sig..(apk.len() - content)]; let mut sha1 = Sha1::new(); sha1.update(control_data); let sha1 = sha1.finalize(); Ok(sha1.to_vec()) } pub async fn detect_installed(container: &Container) -> Result> { let buf = container .exec( &["apk", "info", "-v"], container::Exec { capture_stdout: true, ..Default::default() }, ) .await?; let buf = String::from_utf8(buf).context("Failed to decode apk output as utf8")?; let installed = buf.lines().map(String::from).collect(); Ok(installed) } pub async fn resolve_dependencies( container: &Container, manifest: &PackagesManifest, dependencies: &mut Vec, ) -> Result<()> { info!("Syncing package datatabase..."); container .exec(&["apk", "update"], container::Exec::default()) .await?; let mut dbs = DatabaseCache::default(); { // we only need these files briefly, declare them in a small scope so they get free'd early let repos = container.tar("/etc/apk/repositories").await?; dbs.init_repos_from_container(&repos)?; let tar = container.tar("/var/cache/apk").await?; dbs.import_from_container(&tar)?; } info!("Resolving dependencies..."); let initial_packages = detect_installed(container).await?; // upgrade and install container .exec(&["apk", "upgrade"], container::Exec::default()) .await?; let mut cmd = vec!["apk", "add", "--"]; for dep in &manifest.dependencies { cmd.push(dep.as_str()); } container.exec(&cmd, container::Exec::default()).await?; // detect dependencies let packages_afterwards = detect_installed(container).await?; let new_packages = packages_afterwards.difference(&initial_packages); info!("Calculating package checksums..."); let client = http::Client::new()?; let alpine_cache_dir = paths::alpine_cache_dir()?; for pkg_identifier in new_packages { let pkg = dbs.get(pkg_identifier)?; debug!("Detected dependency: {pkg:?}"); let url = format!( "{}/{}/{}-{}.apk", pkg.repo_url, pkg.arch, pkg.name, pkg.version ); let sha256 = if let Some(sha256) = alpine_cache_dir.sha1_read_link(&pkg.checksum).await? { sha256 } else { let mut buf = Vec::new(); let mut response = client .request(&url) .await .with_context(|| anyhow!("Failed to download package from url: {:?}", url))?; let mut sha256 = Sha256::new(); while let Some(chunk) = response .chunk() .await .context("Failed to read from download stream")? { buf.extend(&chunk); sha256.update(&chunk); } let sha256 = hex::encode(sha256.finalize()); let sha1 = hex::encode(&calculate_checksum_for_apk(&buf)?); if sha1 != pkg.checksum { bail!("Downloaded package (checksum={sha1:?} does not match checksum in APKINDEX (checksum={:?})", pkg.checksum ); } let (sha1_path, sha256_path) = alpine_cache_dir.sha1_to_sha256(&pkg.checksum, &sha256)?; let parent = sha1_path .parent() .context("Failed to determine parent directory")?; fs::create_dir_all(parent).await.with_context(|| { anyhow!("Failed to create parent directories for file: {sha1_path:?}") })?; fs::symlink(sha256_path, sha1_path) .await .context("Failed to create sha1 symlink")?; sha256 }; // record provides if it mentions a dependency let mut provides = Vec::new(); for value in &pkg.provides { if manifest.dependencies.contains(value) { provides.push(value.to_string()); } } dependencies.push(PackageLock { name: pkg.name.to_string(), version: pkg.version.to_string(), system: "alpine".to_string(), url, provides, sha256, signature: None, installed: false, }); } Ok(()) } pub async fn resolve( update: &args::Update, manifest: &PackagesManifest, container: &ContainerLock, dependencies: &mut Vec, ) -> Result<()> { let container = Container::create( &container.image, container::Config { mounts: &[], expose_fuse: false, }, ) .await?; container .run( resolve_dependencies(&container, manifest, dependencies), update.keep, ) .await } #[cfg(test)] mod tests { use super::*; #[test] fn test_checksum_from_apk() -> Result<()> { let checksum = decode_apk_checksum("Q10cGs1h9J5440p6BRXhZC8FO7pVg=")?; let calculated = calculate_checksum_for_apk(crate::test_data::ALPINE_APK_EXAMPLE)?; assert_eq!(checksum, calculated); Ok(()) } } repro-env-0.4.1/src/resolver/archlinux.rs000064400000000000000000000343041046102023000165420ustar 00000000000000use crate::args; use crate::container::{self, Container}; use crate::errors::*; use crate::lockfile::{ContainerLock, PackageLock}; use crate::manifest::PackagesManifest; use flate2::read::GzDecoder; use std::collections::{HashMap, HashSet}; use std::io::Read; #[derive(Debug, Default, PartialEq)] pub struct Package { pub values: HashMap>, } impl Package { pub fn parse(buf: &str) -> Result { let mut pkg = Self::default(); let mut lines = buf.lines(); while let Some(section) = lines.next() { let mut values = Vec::new(); for line in &mut lines { if line.is_empty() { break; } values.push(line.to_string()); } pkg.values.insert(section.to_string(), values); } Ok(pkg) } pub fn add_values(&mut self, key: &str, values: &[&str]) { let values = values.iter().map(|s| s.to_string()).collect(); self.values.insert(key.to_string(), values); } pub fn single_value(&self, key: &str) -> Result<&str> { let values = self .values .get(key) .with_context(|| anyhow!("Failed to find key in package metadata: {key:?}"))?; let mut values = values.iter(); let value = values .next() .with_context(|| anyhow!("No value available for {key:?}"))?; if let Some(trailing) = values.next() { bail!("Unexpected trailing value in {key:?}: {trailing:?}"); } Ok(value) } pub fn name(&self) -> Result<&str> { self.single_value("%NAME%") } pub fn archive_url(&self) -> Result { let filename = self.single_value("%FILENAME%")?; let pkgname = self.name()?; let idx = pkgname .chars() .next() .context("Name for package is empty")?; Ok(format!( "https://archive.archlinux.org/packages/{idx}/{pkgname}/{filename}" )) } pub fn sha256(&self) -> Result<&str> { self.single_value("%SHA256SUM%") } pub fn signature(&self) -> Result<&str> { self.single_value("%PGPSIG%") } } #[derive(Debug, Default)] pub struct DatabaseCache { imported_repositories: HashSet, packages: HashMap, } impl DatabaseCache { pub fn has_repo(&self, repo: &str) -> bool { self.imported_repositories.contains(repo) } pub fn import_repo(&mut self, repo: &str, buf: &[u8]) -> Result<()> { let d = GzDecoder::new(buf); let mut tar = tar::Archive::new(d); for entry in tar.entries()? { let mut entry = entry?; if entry.header().entry_type() == tar::EntryType::Regular { let mut buf = String::new(); trace!("Reading package from archive: {:?}", entry.path()); entry .read_to_string(&mut buf) .context("Failed to read database entry")?; let pkg = Package::parse(&buf).context("Failed to parse database entry as package")?; self.packages.insert(pkg.name()?.to_string(), pkg); } } self.imported_repositories.insert(repo.to_string()); Ok(()) } pub fn get_package(&self, name: &str) -> Result<&Package> { self.packages .get(name) .context("Failed to find package in any database: {name:?}") } } pub async fn resolve_dependencies( container: &Container, manifest: &PackagesManifest, dependencies: &mut Vec, ) -> Result<()> { info!("Syncing package datatabase..."); container .exec(&["pacman", "-Sy"], container::Exec::default()) .await?; info!("Resolving dependencies..."); let mut cmd = vec![ "pacman", "-Sup", "--noconfirm", "--print-format", "%r %n %v", "--", ]; for dep in &manifest.dependencies { cmd.push(dep.as_str()); } let buf = container .exec( &cmd, container::Exec { capture_stdout: true, ..Default::default() }, ) .await?; let buf = String::from_utf8(buf).context("Failed to decode pacman output as utf8")?; let mut dbs = DatabaseCache::default(); for line in buf.lines() { let mut line = line.split(' '); let repo = line.next().context("Missing repo in pacman output")?; let name = line.next().context("Missing pkg name in pacman output")?; let version = line.next().context("Missing version in pacman output")?; if let Some(trailing) = line.next() { bail!("Trailing data in pacman output: {trailing:?}"); } debug!("Detected dependency name={name:?} version={version:?} repo={repo:?}"); if !dbs.has_repo(repo) { let buf = container .cat(&format!("/var/lib/pacman/sync/{repo}.db")) .await?; dbs.import_repo(repo, &buf)?; } let pkg = dbs.get_package(name)?; // record provides if it mentions a dependency let mut provides = Vec::new(); for value in pkg.values.get("%PROVIDES%").into_iter().flatten() { if manifest.dependencies.contains(value) { provides.push(value.to_string()); } } dependencies.push(PackageLock { name: name.to_string(), version: version.to_string(), system: "archlinux".to_string(), url: pkg.archive_url()?, provides, sha256: pkg.sha256()?.to_string(), signature: Some(pkg.signature()?.to_string()), installed: false, }); } Ok(()) } pub async fn resolve( update: &args::Update, manifest: &PackagesManifest, container: &ContainerLock, dependencies: &mut Vec, ) -> Result<()> { let container = Container::create( &container.image, container::Config { mounts: &[], expose_fuse: false, }, ) .await?; container .run( resolve_dependencies(&container, manifest, dependencies), update.keep, ) .await } #[cfg(test)] mod tests { use super::*; use flate2::write::GzEncoder; #[test] fn parse_pkg_entry() -> Result<()> { let buf = r#"%FILENAME% zstd-1.5.5-1-x86_64.pkg.tar.zst %NAME% zstd %BASE% zstd %VERSION% 1.5.5-1 %DESC% Zstandard - Fast real-time compression algorithm %CSIZE% 493009 %ISIZE% 1500453 %MD5SUM% 2ba620ed7816b97bcad1a721a2a9f6c4 %SHA256SUM% 1891970afabc725e72c6a9bb2c127d906c1d3cc70309336fbe87adbd460c05b8 %PGPSIG% iQEzBAABCgAdFiEE5JnHn1PJalTlcv7hwGCGM3xQdz4FAmQ79ZMACgkQwGCGM3xQdz4V+Qf/Yz7Y+3WwSDKtspwcaEr3j95n1nN5+SAThl/OHe94WwmInDWV09GwM+Lrw6Y1RFDK1PI1ZLON3hOo/81udW0uCHJ4n0bnU/2x3B4UW82dcBqFBjiEqNEF1x6KcQGf9PE9seZndsiAxVzrbEH9u48RIHx0SuwWnzlryCoHPYTgYsPrpkH0IzLUerP2Lc8rjUR2eAKn6zoomb3mR74dPNMn2yx9gS0l+79EshQR8kWtOVvTv7xgRriWeJMBNoTTvDfiDq5B8395vPaBmSfrU0O3tvVF3eDAGtpxIb8hqfhtRqy3XqTcRrYaoj44KtJraGCbq5DrsImEdx5byS7qBhoheQ== %URL% https://facebook.github.io/zstd/ %LICENSE% BSD GPL2 %ARCH% x86_64 %BUILDDATE% 1681646714 %PACKAGER% Jelle van der Waa %PROVIDES% libzstd.so=1-64 %DEPENDS% glibc gcc-libs zlib xz lz4 %MAKEDEPENDS% cmake gtest ninja "#; let pkg = Package::parse(buf)?; assert_eq!(pkg.name()?, "zstd"); assert_eq!( pkg.archive_url()?, "https://archive.archlinux.org/packages/z/zstd/zstd-1.5.5-1-x86_64.pkg.tar.zst" ); assert_eq!( pkg.sha256()?, "1891970afabc725e72c6a9bb2c127d906c1d3cc70309336fbe87adbd460c05b8" ); assert_eq!(pkg.signature()?, "iQEzBAABCgAdFiEE5JnHn1PJalTlcv7hwGCGM3xQdz4FAmQ79ZMACgkQwGCGM3xQdz4V+Qf/Yz7Y+3WwSDKtspwcaEr3j95n1nN5+SAThl/OHe94WwmInDWV09GwM+Lrw6Y1RFDK1PI1ZLON3hOo/81udW0uCHJ4n0bnU/2x3B4UW82dcBqFBjiEqNEF1x6KcQGf9PE9seZndsiAxVzrbEH9u48RIHx0SuwWnzlryCoHPYTgYsPrpkH0IzLUerP2Lc8rjUR2eAKn6zoomb3mR74dPNMn2yx9gS0l+79EshQR8kWtOVvTv7xgRriWeJMBNoTTvDfiDq5B8395vPaBmSfrU0O3tvVF3eDAGtpxIb8hqfhtRqy3XqTcRrYaoj44KtJraGCbq5DrsImEdx5byS7qBhoheQ=="); assert!(pkg.single_value("%DEPENDS%").is_err()); let mut expected = Package::default(); expected.add_values("%FILENAME%", &["zstd-1.5.5-1-x86_64.pkg.tar.zst"]); expected.add_values("%NAME%", &["zstd"]); expected.add_values("%BASE%", &["zstd"]); expected.add_values("%VERSION%", &["1.5.5-1"]); expected.add_values( "%DESC%", &["Zstandard - Fast real-time compression algorithm"], ); expected.add_values("%CSIZE%", &["493009"]); expected.add_values("%ISIZE%", &["1500453"]); expected.add_values("%MD5SUM%", &["2ba620ed7816b97bcad1a721a2a9f6c4"]); expected.add_values( "%SHA256SUM%", &["1891970afabc725e72c6a9bb2c127d906c1d3cc70309336fbe87adbd460c05b8"], ); expected.add_values("%PGPSIG%", &[ "iQEzBAABCgAdFiEE5JnHn1PJalTlcv7hwGCGM3xQdz4FAmQ79ZMACgkQwGCGM3xQdz4V+Qf/Yz7Y+3WwSDKtspwcaEr3j95n1nN5+SAThl/OHe94WwmInDWV09GwM+Lrw6Y1RFDK1PI1ZLON3hOo/81udW0uCHJ4n0bnU/2x3B4UW82dcBqFBjiEqNEF1x6KcQGf9PE9seZndsiAxVzrbEH9u48RIHx0SuwWnzlryCoHPYTgYsPrpkH0IzLUerP2Lc8rjUR2eAKn6zoomb3mR74dPNMn2yx9gS0l+79EshQR8kWtOVvTv7xgRriWeJMBNoTTvDfiDq5B8395vPaBmSfrU0O3tvVF3eDAGtpxIb8hqfhtRqy3XqTcRrYaoj44KtJraGCbq5DrsImEdx5byS7qBhoheQ=="]); expected.add_values("%URL%", &["https://facebook.github.io/zstd/"]); expected.add_values("%LICENSE%", &["BSD", "GPL2"]); expected.add_values("%ARCH%", &["x86_64"]); expected.add_values("%BUILDDATE%", &["1681646714"]); expected.add_values("%PACKAGER%", &["Jelle van der Waa "]); expected.add_values("%PROVIDES%", &["libzstd.so=1-64"]); expected.add_values("%DEPENDS%", &["glibc", "gcc-libs", "zlib", "xz", "lz4"]); expected.add_values("%MAKEDEPENDS%", &["cmake", "gtest", "ninja"]); assert_eq!(pkg, expected); Ok(()) } #[test] fn test_database_cache_import() -> Result<()> { let mut db = DatabaseCache::default(); assert!(!db.has_repo("core")); let data = { let mut tar = tar::Builder::new(GzEncoder::new(Vec::new(), flate2::Compression::default())); let data = br#"%FILENAME% rust-1:1.70.0-1-x86_64.pkg.tar.zst %NAME% rust %BASE% rust %VERSION% 1:1.70.0-1 %DESC% Systems programming language focused on safety, speed and concurrency %CSIZE% 90509601 %ISIZE% 483950051 %MD5SUM% a8498a6e40c64d7b08d493133941e918 %SHA256SUM% 8d018b14d2226d76ee46ecd6e28f51ddfa7bfd930463e517eabd5d86f8a17851 %PGPSIG% iIsEABYIADMWIQQGaHodnU+rCLUP2Ss7lKgOUKR3xwUCZHkDRRUcaGVmdGlnQGFyY2hsaW51eC5vcmcACgkQO5SoDlCkd8eCrQEA8y2X/SVbHhchDdfBUp+KBOFoqN63haT6TNq7MIFDvXoA/AwzQe1rwL0RfvxMh130A2wzrid77YXTOjk36QHPmGIL %URL% https://www.rust-lang.org/ %LICENSE% Apache MIT %ARCH% x86_64 %BUILDDATE% 1685646983 %PACKAGER% Jan Alexander Steffens (heftig) %REPLACES% cargo cargo-tree rust-docs<1:1.56.1-3 rustfmt %CONFLICTS% cargo rust-docs<1:1.56.1-3 rustfmt %PROVIDES% cargo rustfmt %DEPENDS% curl gcc gcc-libs libssh2 llvm-libs %OPTDEPENDS% gdb: rust-gdb script lldb: rust-lldb script %MAKEDEPENDS% cmake lib32-gcc-libs libffi lld llvm musl ninja perl python rust wasi-libc %CHECKDEPENDS% gdb procps-ng "#; let mut header = tar::Header::new_gnu(); header.set_path("rust-1:1.70.0-1/desc")?; header.set_size(data.len() as u64); header.set_cksum(); tar.append(&header, &data[..])?; tar.into_inner()?.finish()? }; db.import_repo("core", &data)?; assert!(db.has_repo("core")); let pkg = db.get_package("rust")?; let mut expected = Package::default(); expected.add_values("%FILENAME%", &["rust-1:1.70.0-1-x86_64.pkg.tar.zst"]); expected.add_values("%NAME%", &["rust"]); expected.add_values("%BASE%", &["rust"]); expected.add_values("%VERSION%", &["1:1.70.0-1"]); expected.add_values( "%DESC%", &["Systems programming language focused on safety, speed and concurrency"], ); expected.add_values("%CSIZE%", &["90509601"]); expected.add_values("%ISIZE%", &["483950051"]); expected.add_values("%MD5SUM%", &["a8498a6e40c64d7b08d493133941e918"]); expected.add_values( "%SHA256SUM%", &["8d018b14d2226d76ee46ecd6e28f51ddfa7bfd930463e517eabd5d86f8a17851"], ); expected.add_values("%PGPSIG%", &["iIsEABYIADMWIQQGaHodnU+rCLUP2Ss7lKgOUKR3xwUCZHkDRRUcaGVmdGlnQGFyY2hsaW51eC5vcmcACgkQO5SoDlCkd8eCrQEA8y2X/SVbHhchDdfBUp+KBOFoqN63haT6TNq7MIFDvXoA/AwzQe1rwL0RfvxMh130A2wzrid77YXTOjk36QHPmGIL"]); expected.add_values("%URL%", &["https://www.rust-lang.org/"]); expected.add_values("%LICENSE%", &["Apache", "MIT"]); expected.add_values("%ARCH%", &["x86_64"]); expected.add_values("%BUILDDATE%", &["1685646983"]); expected.add_values( "%PACKAGER%", &["Jan Alexander Steffens (heftig) "], ); expected.add_values( "%REPLACES%", &["cargo", "cargo-tree", "rust-docs<1:1.56.1-3", "rustfmt"], ); expected.add_values("%CONFLICTS%", &["cargo", "rust-docs<1:1.56.1-3", "rustfmt"]); expected.add_values("%PROVIDES%", &["cargo", "rustfmt"]); expected.add_values( "%DEPENDS%", &["curl", "gcc", "gcc-libs", "libssh2", "llvm-libs"], ); expected.add_values( "%OPTDEPENDS%", &["gdb: rust-gdb script", "lldb: rust-lldb script"], ); expected.add_values( "%MAKEDEPENDS%", &[ "cmake", "lib32-gcc-libs", "libffi", "lld", "llvm", "musl", "ninja", "perl", "python", "rust", "wasi-libc", ], ); expected.add_values("%CHECKDEPENDS%", &["gdb", "procps-ng"]); assert_eq!(pkg, &expected); Ok(()) } } repro-env-0.4.1/src/resolver/container.rs000064400000000000000000000014001046102023000165160ustar 00000000000000use crate::args; use crate::container; use crate::container::ImageRef; use crate::errors::*; use crate::lockfile::ContainerLock; use crate::manifest::Manifest; pub async fn resolve(args: &args::Update, manifest: &Manifest) -> Result { let image = manifest.container.image.clone(); if !args.no_pull { container::pull(&image).await?; } let resolved = container::inspect(&image).await?; let digest = &resolved.digest; let mut image_ref = image.parse::()?; image_ref.tag = None; image_ref.digest = Some(digest.to_string()); let pinned_image = image_ref.to_string(); info!("Resolved image reference {:?} to {:?}", image, pinned_image); Ok(ContainerLock { image: pinned_image, }) } repro-env-0.4.1/src/resolver/debian.rs000064400000000000000000000436361046102023000157770ustar 00000000000000use crate::args; use crate::container::{self, Container}; use crate::errors::*; use crate::http; use crate::lockfile::{ContainerLock, PackageLock}; use crate::manifest::PackagesManifest; use crate::paths; use serde::Deserialize; use sha1::Sha1; use sha2::{Digest, Sha256}; use std::collections::HashMap; use std::io::prelude::*; use std::io::Lines; use tokio::fs; #[derive(Debug, Deserialize)] pub struct JsonSnapshotInfo { pub result: Vec, } #[derive(Debug, Deserialize)] pub struct JsonSnapshotPkg { pub archive_name: String, pub first_seen: String, pub name: String, pub path: String, pub size: i64, } #[derive(Debug, Clone, PartialEq)] pub struct PkgEntry { name: String, version: String, provides: Vec, sha256: String, } #[derive(Debug, Default, PartialEq)] pub struct PkgDatabase { pkgs: HashMap, } impl PkgDatabase { pub fn import_lz4(&mut self, reader: R) -> Result<()> { let rdr = lz4_flex::frame::FrameDecoder::new(reader); self.import_lines_stream(rdr.lines()) } pub fn import_lines_stream(&mut self, mut lines: Lines) -> Result<()> { while let Some(line) = lines.next() { let line = line?; trace!("Found line in debian package database: {line:?}"); let Some(name) = line.strip_prefix("Package: ") else { bail!("Unexpected line in database (expected `Package: `): {line:?}") }; let mut version = None; let mut filename = None; let mut provides = Vec::new(); let mut sha256 = None; for line in &mut lines { let line = line?; trace!("Found line in debian package database: {line:?}"); if line.is_empty() { break; } else if let Some(value) = line.strip_prefix("Version: ") { version = Some(value.to_string()); } else if let Some(value) = line.strip_prefix("Filename: ") { let value = value .rsplit_once('/') .map(|(_, filename)| filename) .unwrap_or(value); filename = Some(value.to_string()); } else if let Some(value) = line.strip_prefix("Provides: ") { for entry in value.split(", ") { let (name, _) = entry.split_once(' ').unwrap_or((entry, "")); provides.push(name.to_string()); } } else if let Some(value) = line.strip_prefix("SHA256: ") { sha256 = Some(value.to_string()); } } let filename = filename.context("Package database entry is missing filename")?; let new = PkgEntry { name: name.to_string(), version: version.context("Package database entry is missing version")?, provides, sha256: sha256.context("Package database entry is missing sha256")?, }; let old = self.pkgs.insert(filename.to_string(), new.clone()); if let Some(old) = old { // it's only a problem if they differ if old != new { bail!("Filename is not unique in package database: filename={filename:?}, old={old:?}, new={new:?}"); } } } Ok(()) } pub fn import_tar(buf: &[u8]) -> Result { let mut tar = tar::Archive::new(buf); let mut db = Self::default(); for entry in tar.entries()? { let entry = entry?; let path = entry .header() .path() .context("Filename was not valid utf-8")?; let Some(extension) = path.extension() else { continue; }; if extension.to_str() == Some("lz4") { db.import_lz4(entry)?; } } Ok(db) } pub fn find_by_filename(&self, filename: &str) -> Result<&PkgEntry> { let entry = self .pkgs .get(filename) .context("Failed to find package database entry for: {filename:?}")?; Ok(entry) } pub fn find_by_apt_output(&self, line: &str) -> Result<(String, &PkgEntry)> { let mut line = line.split(' '); let url = line.next().context("Missing url in apt output")?; let filename = line.next().context("Missing filename in apt output")?; let _size = line.next().context("Missing size in apt output")?; let _md5sum = line.next().context("Missing md5sum in apt output")?; if let Some(trailing) = line.next() { bail!("Trailing data in apt output: {trailing:?}"); } let url = url.strip_prefix('\'').unwrap_or(url); let url = url.strip_suffix('\'').unwrap_or(url); debug!("Detected dependency filename={filename:?} url={url:?}"); let package = { let url = url .parse::() .context("Failed to parse as url")?; let filename = url .path_segments() .context("Failed to get path from url")? .last() .context("Failed to get filename from url")?; let filename = urlencoding::decode(filename).context("Failed to url decode filename")?; self.find_by_filename(&filename).with_context(|| { anyhow!("Failed to find package database entry for file: {filename:?}") })? }; Ok((url.to_string(), package)) } } pub async fn resolve_dependencies( container: &Container, manifest: &PackagesManifest, dependencies: &mut Vec, ) -> Result<()> { info!("Update package datatabase..."); container .exec(&["apt-get", "update"], container::Exec::default()) .await?; info!("Importing package database..."); let tar = container.tar("/var/lib/apt/lists").await?; let db = PkgDatabase::import_tar(&tar)?; info!("Resolving dependencies..."); let mut cmd = vec![ "apt-get", "-qq", "--print-uris", "--no-install-recommends", "upgrade", "--", ]; for dep in &manifest.dependencies { cmd.push(dep.as_str()); } let buf = container .exec( &cmd, container::Exec { capture_stdout: true, ..Default::default() }, ) .await?; let buf = String::from_utf8(buf).context("Failed to decode pacman output as utf8")?; let client = http::Client::new()?; let pkgs_cache_dir = paths::pkgs_cache_dir()?; for line in buf.lines() { let (url, package) = db.find_by_apt_output(line)?; let path = pkgs_cache_dir.sha256_path(&package.sha256)?; let buf = if path.exists() { fs::read(path).await? } else { let buf = client.fetch(&url).await?.to_vec(); let mut hasher = Sha256::new(); hasher.update(&buf); let result = hex::encode(hasher.finalize()); if result != package.sha256 { bail!( "Mismatch of sha256 checksum, expected={}, downloaded={}", package.sha256, result ); } buf }; let mut hasher = Sha1::new(); hasher.update(&buf); let sha1 = hex::encode(hasher.finalize()); let url = format!("https://snapshot.debian.org/mr/file/{sha1}/info"); let buf = client .fetch(&url) .await .context("Failed to lookup pkg hash on snapshot.debian.org")?; let info = serde_json::from_slice::(&buf) .context("Failed to decode snapshot.debian.org json response")?; let pkg = info .result .first() .context("Could not find package in any snapshots")?; let archive_name = &pkg.archive_name; let first_seen = &pkg.first_seen; let path = &pkg.path; let name = &pkg.name; let url = format!("https://snapshot.debian.org/archive/{archive_name}/{first_seen}{path}/{name}"); // record provides if it mentions a dependency let mut provides = Vec::new(); for value in &package.provides { if manifest.dependencies.contains(value) { provides.push(value.to_string()); } } dependencies.push(PackageLock { name: package.name.to_string(), version: package.version.to_string(), system: "debian".to_string(), url, provides, sha256: package.sha256.to_string(), signature: None, installed: false, }); } Ok(()) } pub async fn resolve( update: &args::Update, manifest: &PackagesManifest, container: &ContainerLock, dependencies: &mut Vec, ) -> Result<()> { let container = Container::create( &container.image, container::Config { mounts: &[], expose_fuse: false, }, ) .await?; container .run( resolve_dependencies(&container, manifest, dependencies), update.keep, ) .await } #[cfg(test)] mod tests { use super::*; use std::io::BufReader; #[test] fn test_pkg_database() -> Result<()> { let lz4 = { let mut w = lz4_flex::frame::FrameEncoder::new(Vec::new()); w.write_all(br#"Package: binutils-aarch64-linux-gnu Source: binutils Version: 2.40-2 Installed-Size: 19242 Maintainer: Matthias Klose Architecture: amd64 Replaces: binutils (<< 2.29-6), binutils-dev (<< 2.38.50.20220609-2) Depends: binutils-common (= 2.40-2), libbinutils (>= 2.39.50), libc6 (>= 2.36), libgcc-s1 (>= 4.2), libjansson4 (>= 2.14), libzstd1 (>= 1.5.2), zlib1g (>= 1:1.1.4) Suggests: binutils-doc (= 2.40-2) Breaks: binutils (<< 2.29-6), binutils-dev (<< 2.38.50.20220609-2) Description: GNU binary utilities, for aarch64-linux-gnu target Multi-Arch: allowed Homepage: https://www.gnu.org/software/binutils/ Description-md5: 102820197d11c3672c0cd4ce0becb720 Section: devel Priority: optional Filename: pool/main/b/binutils/binutils-aarch64-linux-gnu_2.40-2_amd64.deb Size: 3352924 MD5sum: 2c02fdb8d4455ace16be0bb922eb8502 SHA256: 3d6f64a7a4ed6d73719f8fa2e85fd896f58ff7f211a6683942ba93de690aaa66 Package: rustc Version: 1.63.0+dfsg1-2 Installed-Size: 7753 Maintainer: Debian Rust Maintainers Architecture: amd64 Replaces: libstd-rust-dev (<< 1.25.0+dfsg1-2~~) Depends: libc6 (>= 2.34), libgcc-s1 (>= 3.0), libstd-rust-dev (= 1.63.0+dfsg1-2), gcc, libc-dev, binutils (>= 2.26) Recommends: cargo (>= 0.64.0~~), cargo (<< 0.65.0~~), llvm-14 Suggests: lld-14, clang-14 Breaks: libstd-rust-dev (<< 1.25.0+dfsg1-2~~) Description: Rust systems programming language Multi-Arch: allowed Homepage: http://www.rust-lang.org/ Description-md5: 67ca6080eea53dc7f3cdf73bc6b8521e Section: rust Priority: optional Filename: pool/main/r/rustc/rustc_1.63.0+dfsg1-2_amd64.deb Size: 2612712 MD5sum: 5eaa6969388c512a206377bf813ab531 SHA256: 26dd439266153e38d3e6fbe0fe2dbbb41f20994afa688faa71f38427348589ed "#)?; w.finish()? }; let tar = { let mut tar = tar::Builder::new(Vec::new()); let mut header = tar::Header::new_gnu(); header.set_path("deb.debian.org_debian_dists_stable_main_binary-amd64_Packages.lz4")?; header.set_size(lz4.len() as u64); header.set_cksum(); tar.append(&header, &lz4[..])?; tar.into_inner()? }; let db = PkgDatabase::import_tar(&tar)?; let pkgs = { let mut pkgs = HashMap::new(); pkgs.insert( "binutils-aarch64-linux-gnu_2.40-2_amd64.deb".to_string(), PkgEntry { name: "binutils-aarch64-linux-gnu".to_string(), version: "2.40-2".to_string(), provides: vec![], sha256: "3d6f64a7a4ed6d73719f8fa2e85fd896f58ff7f211a6683942ba93de690aaa66" .to_string(), }, ); pkgs.insert( "rustc_1.63.0+dfsg1-2_amd64.deb".to_string(), PkgEntry { name: "rustc".to_string(), version: "1.63.0+dfsg1-2".to_string(), provides: vec![], sha256: "26dd439266153e38d3e6fbe0fe2dbbb41f20994afa688faa71f38427348589ed" .to_string(), }, ); pkgs }; assert_eq!(db, PkgDatabase { pkgs }); Ok(()) } #[test] fn test_pkg_database_apt_output_parser() -> Result<()> { let mut db = PkgDatabase::default(); db.pkgs.insert( "rustc_1.63.0+dfsg1-2_amd64.deb".to_string(), PkgEntry { name: "rustc".to_string(), version: "1.63.0+dfsg1-2".to_string(), provides: vec![], sha256: "26dd439266153e38d3e6fbe0fe2dbbb41f20994afa688faa71f38427348589ed" .to_string(), }, ); let result = db.find_by_apt_output("'http://deb.debian.org/debian/pool/main/r/rustc/rustc_1.63.0%2bdfsg1-2_amd64.deb' rustc_1.63.0+dfsg1-2_amd64.deb 2612712 MD5Sum:5eaa6969388c512a206377bf813ab531")?; assert_eq!( result, ( "http://deb.debian.org/debian/pool/main/r/rustc/rustc_1.63.0%2bdfsg1-2_amd64.deb" .to_string(), &PkgEntry { name: "rustc".to_string(), version: "1.63.0+dfsg1-2".to_string(), provides: vec![], sha256: "26dd439266153e38d3e6fbe0fe2dbbb41f20994afa688faa71f38427348589ed" .to_string(), } ) ); let result = db.find_by_apt_output("'http://deb.debian.org/debian/pool/main/n/non-existant/non-existant_1.2.3_amd64.deb' non-existant_1.2.3_amd64.deb 2612712 MD5Sum:5eaa6969388c512a206377bf813ab531"); assert!(result.is_err()); Ok(()) } #[test] fn test_parse_provides() -> Result<()> { let foo = BufReader::new(r#"Package: librust-repro-env-dev Source: rust-repro-env Version: 0.3.2-1 Installed-Size: 175 Maintainer: Debian Rust Maintainers Architecture: amd64 Provides: librust-repro-env+default-dev (= 0.3.2-1), librust-repro-env-0+default-dev (= 0.3.2-1), librust-repro-env-0-dev (= 0.3.2-1), librust-repro-env-0.3+default-dev (= 0.3.2-1), librust-repro-env-0.3-dev (= 0.3.2-1), librust-repro-env-0.3.2+default-dev (= 0.3.2-1), librust-repro-env-0.3.2-dev (= 0.3.2-1) Depends: librust-anyhow-1+default-dev (>= 1.0.71-~~), librust-ar-0.9+default-dev, librust-bytes-1+default-dev (>= 1.4.0-~~), librust-clap-4+default-dev, librust-clap-4+derive-dev, librust-clap-complete-4+default-dev, librust-clone-file-0.1+default-dev, librust-data-encoding-2+default-dev (>= 2.4.0-~~), librust-dirs-5+default-dev (>= 5.0.1-~~), librust-env-logger-0.10+default-dev, librust-fd-lock-3+default-dev, librust-flate2-1+default-dev (>= 1.0.26-~~), librust-hex-0.4+default-dev (>= 0.4.3-~~), librust-log-0.4+default-dev (>= 0.4.19-~~), librust-lz4-flex-0.11+default-dev (>= 0.11.1-~~), librust-lzma-rs-0.3+default-dev, librust-memchr-2+default-dev (>= 2.5.0-~~), librust-nix-0.26+sched-dev, librust-peekread-0.1+default-dev (>= 0.1.1-~~), librust-reqwest-0.11+rustls-tls-native-roots-dev (>= 0.11.18-~~), librust-reqwest-0.11+stream-dev (>= 0.11.18-~~), librust-reqwest-0.11+tokio-socks-dev (>= 0.11.18-~~), librust-ruzstd-0.4+default-dev, librust-serde-1+default-dev, librust-serde-1+derive-dev, librust-serde-json-1+default-dev, librust-sha1-0.10+default-dev (>= 0.10.5-~~), librust-sha2-0.10+default-dev (>= 0.10.7-~~), librust-tar-0.4+default-dev (>= 0.4.38-~~), librust-tempfile-3+default-dev (>= 3.6.0-~~), librust-tokio-1+default-dev, librust-tokio-1+fs-dev, librust-tokio-1+macros-dev, librust-tokio-1+process-dev, librust-tokio-1+rt-multi-thread-dev, librust-tokio-1+signal-dev, librust-toml-0.7+default-dev, librust-urlencoding-2+default-dev (>= 2.1.2-~~) Description: Dependency lockfiles for reproducible build environments 📦🔒 - Rust source code Multi-Arch: same Description-md5: 1023d39707057b0b09f9d6bf7deeb14e Section: utils Priority: optional Filename: pool/main/r/rust-repro-env/librust-repro-env-dev_0.3.2-1_amd64.deb Size: 40344 MD5sum: 4dafbbe511b9a068728930e6811a0bf0 SHA256: 2bb1befee1b89f0462b74d519be9b8c94c038d7f8a074d050d62985f47ec4164 "#.as_bytes()); let mut db = PkgDatabase::default(); db.import_lines_stream(foo.lines())?; let pkgs = { let mut pkgs = HashMap::new(); pkgs.insert( "librust-repro-env-dev_0.3.2-1_amd64.deb".to_string(), PkgEntry { name: "librust-repro-env-dev".to_string(), version: "0.3.2-1".to_string(), provides: vec![ "librust-repro-env+default-dev".to_string(), "librust-repro-env-0+default-dev".to_string(), "librust-repro-env-0-dev".to_string(), "librust-repro-env-0.3+default-dev".to_string(), "librust-repro-env-0.3-dev".to_string(), "librust-repro-env-0.3.2+default-dev".to_string(), "librust-repro-env-0.3.2-dev".to_string(), ], sha256: "2bb1befee1b89f0462b74d519be9b8c94c038d7f8a074d050d62985f47ec4164" .to_string(), }, ); pkgs }; assert_eq!(db, PkgDatabase { pkgs }); Ok(()) } } repro-env-0.4.1/src/resolver/mod.rs000064400000000000000000000021231046102023000153160ustar 00000000000000pub mod alpine; pub mod archlinux; pub mod container; pub mod debian; use crate::args; use crate::errors::*; use crate::lockfile::Lockfile; use crate::manifest::Manifest; pub async fn resolve(args: &args::Update, manifest: &Manifest) -> Result { let container = container::resolve(args, manifest).await?; let mut dependencies = Vec::new(); if let Some(packages) = &manifest.packages { match packages.system.as_str() { "alpine" => alpine::resolve(args, packages, &container, &mut dependencies).await?, "archlinux" => { archlinux::resolve(args, packages, &container, &mut dependencies).await? } "debian" => debian::resolve(args, packages, &container, &mut dependencies).await?, system => bail!("Unknown package system: {system:?}"), } } dependencies.sort_by(|a, b| { a.name .cmp(&b.name) .then(a.version.cmp(&b.version)) .then(a.system.cmp(&b.system)) }); Ok(Lockfile { container, packages: dependencies, }) } repro-env-0.4.1/src/test_data.rs000064400000000000000000000225441046102023000146570ustar 00000000000000pub const ALPINE_APK_EXAMPLE: &[u8] = &[ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xd3, 0x0b, 0xf6, 0x74, 0xf7, 0xd3, 0x0b, 0x0a, 0x76, 0xd4, 0x4b, 0xcc, 0x29, 0xc8, 0xcc, 0x4b, 0xd5, 0x4d, 0x49, 0x2d, 0x4b, 0xcd, 0x71, 0xc8, 0xc9, 0x2c, 0x2e, 0x29, 0x86, 0x0a, 0xe5, 0x64, 0xe6, 0x95, 0x56, 0xe8, 0xe5, 0x17, 0xa5, 0xeb, 0x9a, 0x19, 0x9a, 0x99, 0xa6, 0xa6, 0x9a, 0x5a, 0xea, 0x15, 0x15, 0x27, 0xea, 0x15, 0x94, 0x26, 0x31, 0x10, 0x07, 0x0c, 0x80, 0xc0, 0xcc, 0xc4, 0x04, 0x4c, 0x03, 0x01, 0x3a, 0x6d, 0x08, 0x62, 0x1b, 0x9a, 0x98, 0x98, 0x99, 0x18, 0x9a, 0x99, 0x19, 0x9b, 0x03, 0xc5, 0x8d, 0x0c, 0x4c, 0x8c, 0x8d, 0x19, 0x14, 0x0c, 0x18, 0xe8, 0x00, 0x4a, 0x8b, 0x4b, 0x12, 0x8b, 0x14, 0x14, 0x18, 0x46, 0x28, 0xb0, 0x4e, 0xd3, 0xcb, 0x7b, 0x2e, 0x79, 0xed, 0xc7, 0x93, 0x73, 0x9a, 0x61, 0xa7, 0xc5, 0x17, 0xa5, 0x86, 0xeb, 0x45, 0xee, 0x30, 0xad, 0xba, 0x72, 0x73, 0xff, 0xdd, 0xee, 0xee, 0x87, 0xce, 0x57, 0xef, 0xd4, 0xa6, 0xcc, 0x48, 0x9b, 0xc7, 0x27, 0x65, 0x7d, 0xf2, 0xc7, 0xdb, 0x07, 0xba, 0x1e, 0x57, 0xa7, 0x86, 0x49, 0x57, 0xcd, 0xcf, 0x70, 0x55, 0xfb, 0xeb, 0x7e, 0x73, 0x55, 0x56, 0xca, 0x6d, 0x59, 0x6f, 0x26, 0xfb, 0xeb, 0x11, 0x72, 0x5f, 0x65, 0xa3, 0xba, 0x22, 0x33, 0x0e, 0x14, 0xbe, 0x4e, 0x3c, 0x68, 0xfd, 0x43, 0xed, 0x8c, 0x8b, 0x71, 0xf4, 0xaf, 0xb9, 0x0b, 0xd7, 0x44, 0x95, 0x7a, 0x74, 0x1c, 0x9e, 0x39, 0x4b, 0x59, 0xfa, 0xf5, 0xa1, 0x3d, 0xd3, 0x84, 0xf7, 0x5c, 0xcc, 0x95, 0x3d, 0xed, 0xfc, 0x61, 0x59, 0xe7, 0x99, 0x98, 0x1d, 0x7c, 0x0f, 0xf5, 0xac, 0xa7, 0x9e, 0x35, 0x12, 0x79, 0xf2, 0x75, 0xd9, 0xe9, 0xd9, 0x53, 0x1d, 0x2b, 0x2b, 0x4e, 0x4d, 0x8d, 0xd9, 0x59, 0x26, 0x53, 0xd6, 0x2b, 0x7e, 0x62, 0xf5, 0xc1, 0xc9, 0x6a, 0x4f, 0xb3, 0xa7, 0x55, 0x5d, 0xdb, 0x39, 0xfb, 0xbf, 0x6b, 0xb8, 0xf4, 0xa5, 0xa9, 0x0f, 0x66, 0x1c, 0x4c, 0x6b, 0xdb, 0x98, 0x90, 0xeb, 0xbd, 0x82, 0x6d, 0x5e, 0x50, 0xd2, 0x85, 0x29, 0xcb, 0xe7, 0xcf, 0x79, 0x72, 0x8b, 0x8f, 0x6d, 0xd9, 0x93, 0x03, 0xd6, 0x76, 0x47, 0xd9, 0x59, 0xd3, 0x74, 0x77, 0x6d, 0x5d, 0x75, 0xf2, 0xda, 0x8b, 0xfd, 0x09, 0x52, 0xb7, 0xe2, 0x3b, 0x72, 0xe6, 0x30, 0x9c, 0x5a, 0x58, 0xee, 0xe7, 0xd6, 0xf3, 0xf0, 0x81, 0xd8, 0x62, 0xf1, 0xaf, 0xe2, 0x71, 0x79, 0xa7, 0x12, 0x4f, 0x37, 0xf6, 0x25, 0x16, 0xca, 0xd7, 0x64, 0xd7, 0xd9, 0xfe, 0x5c, 0x73, 0xed, 0xca, 0x7c, 0x8d, 0x3f, 0x4f, 0xd6, 0x2d, 0xd8, 0x5d, 0xe1, 0x96, 0x27, 0xda, 0x2c, 0xbb, 0xa8, 0x41, 0x2f, 0x77, 0x35, 0x83, 0xcf, 0xb7, 0xad, 0x06, 0x0d, 0xc6, 0xab, 0xc2, 0x3a, 0x5f, 0xc5, 0x4a, 0xbb, 0x2f, 0xda, 0x55, 0xba, 0x76, 0xcb, 0x22, 0xb5, 0x1f, 0x67, 0xdc, 0x2d, 0x66, 0xec, 0x9b, 0xe1, 0x16, 0x70, 0xec, 0x43, 0xc2, 0xb2, 0xe7, 0xdb, 0x67, 0x71, 0xbf, 0xce, 0xd1, 0x3e, 0x70, 0x9d, 0x8d, 0x6b, 0xfd, 0xd5, 0xfd, 0x6c, 0xa5, 0xad, 0xfe, 0x33, 0xbb, 0xd7, 0xfd, 0x3b, 0x69, 0xa0, 0x53, 0x68, 0xdf, 0x7a, 0x6c, 0x86, 0xf8, 0xa1, 0xc2, 0xb8, 0xf9, 0x05, 0xba, 0x47, 0xa7, 0xfc, 0x54, 0x3d, 0x75, 0xf5, 0xd2, 0xc9, 0xb5, 0xde, 0xc7, 0x85, 0x67, 0x4c, 0x3b, 0xe2, 0x73, 0x38, 0xb5, 0xe5, 0xa5, 0x91, 0x4d, 0xc1, 0x19, 0x81, 0x0d, 0xe7, 0xed, 0xf6, 0x26, 0x17, 0x2d, 0xbb, 0xab, 0x50, 0x18, 0x79, 0xf1, 0x8a, 0x52, 0xed, 0xc2, 0xb2, 0x1b, 0xc6, 0x6b, 0xc5, 0x4f, 0xe6, 0x4d, 0xfe, 0x98, 0xbe, 0xc1, 0xbf, 0x82, 0x6d, 0x3d, 0xf3, 0xc9, 0xe5, 0xcb, 0x0c, 0xac, 0xd9, 0x82, 0x57, 0x46, 0x1c, 0x3c, 0x77, 0x9b, 0x5f, 0xe4, 0xb5, 0x28, 0xdb, 0x84, 0x05, 0xf3, 0xbd, 0x67, 0xcd, 0xfb, 0xd7, 0x58, 0xee, 0x21, 0x60, 0x98, 0x9c, 0x6b, 0xc5, 0xf3, 0x33, 0x20, 0x40, 0xf8, 0x25, 0x5f, 0x95, 0xfd, 0xca, 0x33, 0xa7, 0x76, 0xd9, 0x9d, 0x89, 0x0f, 0xab, 0xc8, 0x6b, 0x3d, 0x55, 0xae, 0x7c, 0xe3, 0xd1, 0xbc, 0xb0, 0xfd, 0xe6, 0x8b, 0xe6, 0x3e, 0xbc, 0xb5, 0x8f, 0x7f, 0x7b, 0xd8, 0x8a, 0x3b, 0xb7, 0x7c, 0x6e, 0x4e, 0x37, 0xe1, 0x62, 0xfb, 0xf8, 0x8b, 0xe9, 0xd1, 0x9f, 0x2b, 0x8f, 0xca, 0xa7, 0x7b, 0xca, 0xf7, 0x7c, 0x72, 0x78, 0xa0, 0xf7, 0x40, 0xbe, 0x67, 0x7e, 0x61, 0xa9, 0xd2, 0xc2, 0x39, 0x6c, 0xdf, 0x4c, 0x66, 0xd5, 0x1c, 0x4c, 0xe3, 0x3f, 0x18, 0xf6, 0xfb, 0x92, 0x9b, 0xfc, 0x5d, 0xcf, 0xe4, 0xd0, 0x69, 0x67, 0x1e, 0x2d, 0xdb, 0x0c, 0x00, 0x91, 0x82, 0x74, 0x2c, 0x00, 0x04, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xed, 0x55, 0x6d, 0x6b, 0xdb, 0x30, 0x10, 0xce, 0x67, 0xff, 0x0a, 0x41, 0x3f, 0xc7, 0x91, 0x64, 0x59, 0xb6, 0x43, 0x3b, 0xba, 0x15, 0xd6, 0x95, 0xd1, 0xae, 0x1f, 0xba, 0x1f, 0x70, 0x96, 0xe5, 0x54, 0x44, 0x96, 0x8c, 0x25, 0x97, 0xa4, 0xbf, 0x7e, 0xe7, 0x2c, 0xd0, 0x2d, 0x61, 0x6f, 0xb0, 0x15, 0xb6, 0xee, 0x01, 0x73, 0xc7, 0x73, 0xa7, 0x7b, 0xd1, 0xf9, 0x50, 0xba, 0xb8, 0x85, 0xcd, 0x3b, 0x0d, 0x8d, 0x1e, 0xc2, 0x22, 0xbd, 0x7d, 0x7f, 0x79, 0x75, 0xf3, 0xf6, 0xc3, 0xec, 0xf7, 0x82, 0x22, 0xa4, 0x10, 0x3b, 0x89, 0x38, 0x94, 0x94, 0x72, 0x39, 0x63, 0x42, 0x48, 0xc1, 0xa4, 0xcc, 0x0a, 0xe4, 0x19, 0x13, 0x54, 0xcc, 0xc8, 0x66, 0xf6, 0x0c, 0x18, 0x43, 0x84, 0x01, 0x4b, 0x99, 0xbd, 0x4c, 0x30, 0x46, 0x54, 0x34, 0x9d, 0x3e, 0xa3, 0x09, 0xaa, 0xb0, 0x57, 0x67, 0xff, 0xf1, 0x42, 0xf0, 0x87, 0x56, 0xfe, 0x57, 0xf6, 0x1f, 0x57, 0x5f, 0x1c, 0xec, 0x3f, 0x67, 0x28, 0x08, 0x7d, 0xce, 0xfd, 0x1f, 0xbc, 0x8f, 0xdf, 0xf3, 0xfb, 0x91, 0xfd, 0xb0, 0xb9, 0xbf, 0x04, 0x27, 0xe4, 0x52, 0x3b, 0x3d, 0x40, 0xd4, 0x0d, 0xa9, 0xb7, 0x04, 0xea, 0xd1, 0xd8, 0x86, 0x64, 0x29, 0x63, 0x29, 0x9b, 0x0f, 0x34, 0x39, 0x21, 0x63, 0x30, 0x6e, 0x45, 0x5a, 0x58, 0xeb, 0xe9, 0x0a, 0xc8, 0x03, 0x3e, 0x14, 0xc6, 0x3b, 0xc2, 0xd2, 0x8c, 0xa1, 0xf5, 0x1a, 0xd5, 0xd7, 0xe3, 0x8a, 0x90, 0x82, 0xb0, 0x6c, 0x49, 0xab, 0x25, 0xe3, 0xe4, 0xe3, 0xdd, 0x05, 0xe1, 0x94, 0x67, 0x49, 0xbf, 0x5e, 0x39, 0xe8, 0x34, 0x39, 0x23, 0x60, 0x7b, 0xe3, 0xf4, 0xbc, 0x86, 0xa0, 0x27, 0x16, 0x83, 0x20, 0x89, 0x59, 0xca, 0x34, 0x9b, 0xb2, 0x20, 0xd5, 0xe8, 0xa0, 0x90, 0xbb, 0xd6, 0x11, 0x48, 0x0f, 0x6a, 0x0d, 0x2b, 0x4d, 0x5a, 0x3f, 0x90, 0xce, 0x38, 0xd3, 0x81, 0xdd, 0x47, 0x20, 0xbb, 0x08, 0xe3, 0x60, 0xd1, 0xf5, 0x3e, 0xc6, 0x3e, 0x2c, 0x17, 0x8b, 0xcf, 0x16, 0x6b, 0xdc, 0xb8, 0x49, 0xfd, 0xb0, 0x4a, 0x76, 0x2d, 0x34, 0xd8, 0x11, 0xfa, 0x30, 0x59, 0x31, 0xc1, 0xb2, 0x22, 0xe7, 0xc9, 0x3e, 0xe8, 0x94, 0xf8, 0xcd, 0xe4, 0xe1, 0x1f, 0x51, 0x3f, 0xdd, 0x17, 0xd6, 0xe8, 0x07, 0x6d, 0xcf, 0xad, 0x09, 0x31, 0xa4, 0x07, 0xf1, 0x5e, 0x25, 0xc1, 0x3c, 0x4e, 0xb1, 0x04, 0xad, 0x64, 0x02, 0x83, 0xba, 0x47, 0xdd, 0xf9, 0x49, 0x49, 0xfc, 0x60, 0x56, 0xc6, 0x1d, 0xf4, 0xa7, 0x7c, 0xd7, 0x99, 0x88, 0x64, 0x51, 0x8a, 0xac, 0xa5, 0xa5, 0x2c, 0xb3, 0xa6, 0x6d, 0xa9, 0xd2, 0xb2, 0xc8, 0x2a, 0xa1, 0xeb, 0xb2, 0xd6, 0xbc, 0xcd, 0xcb, 0x36, 0xaf, 0x4a, 0x49, 0x25, 0xab, 0xaa, 0xa4, 0x03, 0xe3, 0x22, 0x7e, 0xbb, 0xe2, 0x6e, 0x20, 0x82, 0x03, 0x6d, 0xc9, 0x85, 0xef, 0x81, 0x9c, 0x3a, 0x85, 0xe2, 0xfc, 0xa8, 0x26, 0x6b, 0x94, 0x76, 0x61, 0x2a, 0xeb, 0xfa, 0xea, 0x2e, 0x19, 0x74, 0x6f, 0x41, 0xe9, 0xf0, 0x75, 0x25, 0x16, 0xb6, 0x7e, 0x8c, 0x49, 0xa3, 0x7b, 0xed, 0x9a, 0x9f, 0x32, 0x29, 0xef, 0xda, 0x23, 0x72, 0xd0, 0x56, 0x4f, 0x7d, 0x3d, 0xf1, 0xfd, 0x7a, 0x1e, 0xbd, 0xb7, 0xe1, 0x89, 0xaa, 0xc7, 0xb0, 0xad, 0xfd, 0xe6, 0x88, 0x98, 0x77, 0x78, 0xb3, 0x73, 0x8f, 0xe4, 0xa0, 0x8e, 0x8d, 0xdf, 0xe2, 0xc3, 0x68, 0x9a, 0x27, 0xd6, 0x9a, 0x5a, 0xcd, 0xc7, 0x68, 0xbe, 0xcc, 0xb7, 0x3f, 0x79, 0x42, 0x60, 0x8c, 0xbe, 0xc3, 0x67, 0x4b, 0x81, 0xb5, 0x5b, 0xd2, 0xe8, 0xa8, 0x15, 0xfe, 0xca, 0xcb, 0x04, 0xe7, 0x0f, 0xf7, 0x10, 0xa6, 0x59, 0xb5, 0x05, 0x15, 0x4d, 0x41, 0x33, 0xa8, 0x54, 0xc9, 0x4b, 0xc9, 0x5b, 0x59, 0x32, 0x0a, 0x55, 0x55, 0x0b, 0xc9, 0xa5, 0xe0, 0x2c, 0x87, 0x8a, 0xe7, 0x79, 0xce, 0x25, 0x17, 0x80, 0xd3, 0x51, 0x5c, 0x30, 0xa0, 0x54, 0x15, 0x80, 0x53, 0xd3, 0xb5, 0xaa, 0xff, 0xe9, 0xd7, 0xf0, 0x13, 0xe6, 0x95, 0x32, 0x8e, 0x00, 0x0a, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xed, 0xd3, 0x4d, 0x4a, 0x03, 0x31, 0x14, 0x07, 0xf0, 0xec, 0x8a, 0x05, 0x77, 0x2e, 0x5c, 0xce, 0x05, 0x9c, 0xbe, 0x4c, 0x32, 0x99, 0xc9, 0xa2, 0x60, 0x77, 0x85, 0x0a, 0x2d, 0x54, 0xb7, 0xc5, 0xcc, 0x24, 0x83, 0x52, 0x86, 0xc2, 0x7c, 0x40, 0xbd, 0x85, 0x1b, 0x4f, 0xe0, 0x01, 0x3c, 0x82, 0xd7, 0x10, 0x0f, 0xa1, 0x57, 0x30, 0x15, 0x37, 0x76, 0xd1, 0x22, 0xb4, 0x85, 0xe2, 0xff, 0xb7, 0x79, 0x2f, 0x1f, 0x8b, 0x17, 0xc8, 0x3f, 0xec, 0x4d, 0xcc, 0x72, 0xe8, 0x8c, 0x75, 0x55, 0xdd, 0x0b, 0x6d, 0x5b, 0x96, 0x0f, 0x6c, 0xd7, 0xc8, 0x53, 0x52, 0x7e, 0x57, 0x6f, 0xbd, 0x12, 0x17, 0x11, 0xe3, 0x52, 0x2a, 0xc9, 0x95, 0x12, 0x89, 0xdf, 0xe7, 0x7e, 0x45, 0x2c, 0x58, 0xb2, 0x03, 0x68, 0xeb, 0xc6, 0x54, 0x7e, 0x14, 0xf6, 0x3f, 0x71, 0x1e, 0xe4, 0xcd, 0x7d, 0xe9, 0xfa, 0xd4, 0xf5, 0xad, 0xf9, 0x69, 0x55, 0x1a, 0x0c, 0x26, 0xa3, 0x8b, 0xeb, 0xf1, 0xf8, 0x6a, 0x1a, 0xe6, 0x77, 0x2e, 0x9f, 0xd7, 0x6d, 0x19, 0x4e, 0x87, 0x03, 0xde, 0xb7, 0x46, 0x68, 0x23, 0x9c, 0x8b, 0x9d, 0xca, 0x64, 0x46, 0x56, 0x44, 0x71, 0x9c, 0x15, 0xae, 0xd0, 0xb1, 0x22, 0x9e, 0x6a, 0x32, 0x85, 0x4d, 0x29, 0x21, 0xdd, 0x65, 0x70, 0x04, 0xf6, 0x14, 0xf9, 0x3f, 0xe5, 0x7f, 0xd5, 0xff, 0xce, 0x7f, 0x22, 0x05, 0x67, 0xc1, 0x2a, 0x93, 0xfe, 0x1b, 0x46, 0x97, 0xec, 0x8c, 0xbd, 0xeb, 0xe7, 0x8f, 0xd9, 0x7c, 0x74, 0x1a, 0xdd, 0xbc, 0x7e, 0x3e, 0xdd, 0x9e, 0x3f, 0xbe, 0xbc, 0x75, 0x4e, 0x76, 0x9a, 0xff, 0x6a, 0xb1, 0x68, 0x36, 0xdd, 0xdb, 0x76, 0xbe, 0xfe, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xf8, 0x02, 0xd7, 0x2b, 0xfd, 0xaf, 0x00, 0x28, 0x00, 0x00, ]; repro-env-0.4.1/src/update.rs000064400000000000000000000013271046102023000141650ustar 00000000000000use crate::args; use crate::container; use crate::errors::*; use crate::manifest::Manifest; use crate::resolver; use std::path::Path; use tokio::fs; pub async fn update(update: &args::Update) -> Result<()> { container::test_for_unprivileged_userns_clone().await?; let manifest_path = Path::new("repro-env.toml"); let lockfile_path = Path::new("repro-env.lock"); let manifest = Manifest::read_from_file(manifest_path).await?; let lockfile = resolver::resolve(update, &manifest).await?; trace!("Resolved manifest into lockfile: {lockfile:?}"); debug!("Updating dependency lockfile: {lockfile_path:?}"); let buf = lockfile.serialize()?; fs::write(lockfile_path, buf).await?; Ok(()) } repro-env-0.4.1/src/utils.rs000064400000000000000000000004221046102023000140360ustar 00000000000000use crate::errors::*; use flate2::bufread::GzDecoder; use std::io::{BufRead, Read}; pub fn read_gzip_to_end(reader: &mut R) -> Result> { let mut buf = Vec::new(); let mut gz = GzDecoder::new(reader); gz.read_to_end(&mut buf)?; Ok(buf) }