Compare commits

...

14 Commits

Author SHA1 Message Date
Taiki Endo
0d865d5cc6 Release 2.69.7 2026-03-23 23:19:45 +09:00
Taiki Endo
78e479f933 Update prek@latest to 0.3.8 2026-03-23 13:47:38 +00:00
Taiki Endo
3da8dc3058 Update cspell dictionary 2026-03-23 22:31:39 +09:00
Taiki Endo
dcad28ece2 Update mise@latest to 2026.3.12 2026-03-23 00:55:49 +00:00
Taiki Endo
2c637c3acd Remove duplicated retry 2026-03-22 22:21:56 +09:00
Taiki Endo
ef1e8ec3f0 ci: Update config 2026-03-22 22:20:46 +09:00
Taiki Endo
0673e7604e Update cargo-valgrind@latest to 2.4.1 2026-03-22 12:28:36 +00:00
Taiki Endo
06203676c6 Release 2.69.6 2026-03-22 02:50:18 +09:00
Taiki Endo
c35d18270e Support signature verification for mise and syft 2026-03-22 01:51:55 +09:00
Taiki Endo
525387f706 codegen: Clean up error handling and move some checks to appropriate
place

Unless error handling is done in particular care, in private tools this
approach tends to make debugging easier when failed.
2026-03-22 00:24:42 +09:00
Taiki Endo
7a6eff0bac Update cargo-binstall@latest to 1.17.8 2026-03-21 09:24:46 +00:00
Taiki Endo
458413b553 Update tombi@latest to 0.9.9 2026-03-21 05:38:52 +00:00
Taiki Endo
b988c18e3d Release 2.69.5 2026-03-21 12:38:29 +09:00
Taiki Endo
5fe6797db0 Update cargo-nextest@latest to 0.9.132 2026-03-21 00:46:56 +00:00
18 changed files with 560 additions and 126 deletions

View File

@@ -20,16 +20,18 @@ libicu
linkcheck linkcheck
mdbook mdbook
microdnf microdnf
minisig
mirrorlist mirrorlist
nextest nextest
pluginconf pluginconf
ppcle ppcle
prek prek
quickinstall quickinstall
rclone
rdme rdme
rootfs rootfs
sccache sccache
SHASUMS
sigstore
syft syft
tombi tombi
udeps udeps

View File

@@ -11,7 +11,7 @@ on:
- dev - dev
- ci-* - ci-*
schedule: schedule:
- cron: '0 0 * * *' - cron: '0 2 * * *'
workflow_dispatch: workflow_dispatch:
env: env:

View File

@@ -44,4 +44,5 @@ jobs:
permissions: permissions:
contents: write # for taiki-e/create-gh-release-action contents: write # for taiki-e/create-gh-release-action
id-token: write # for rust-lang/crates-io-auth-action id-token: write # for rust-lang/crates-io-auth-action
attestations: write # unused (used when options for uploading binaries are set)
secrets: inherit secrets: inherit

View File

@@ -10,6 +10,30 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com
## [Unreleased] ## [Unreleased]
## [2.69.7] - 2026-03-23
- Update `prek@latest` to 0.3.8.
- Update `mise@latest` to 2026.3.12.
- Update `cargo-valgrind@latest` to 2.4.1.
## [2.69.6] - 2026-03-21
- Support signature verification for `mise` and `syft`. ([#1611](https://github.com/taiki-e/install-action/pull/1611))
- Update `mise@latest` to 2026.3.10.
- Update `knope@latest` to 0.22.4.
- Update `cargo-binstall@latest` to 1.17.8.
- Update `tombi@latest` to 0.9.9.
## [2.69.5] - 2026-03-21
- Update `cargo-nextest@latest` to 0.9.132.
## [2.69.4] - 2026-03-20 ## [2.69.4] - 2026-03-20
- Support artifact attestations verification for `biome`, `cargo-cyclonedx`, `cargo-hack`, `cargo-llvm-cov`, `cargo-minimal-versions`, `cargo-no-dev-deps`, `martin`, `parse-changelog`, `parse-dockerfile`, `prek`, `uv`, `wasmtime`, `zizmor`, and `zola`. ([#1606](https://github.com/taiki-e/install-action/pull/1606)) - Support artifact attestations verification for `biome`, `cargo-cyclonedx`, `cargo-hack`, `cargo-llvm-cov`, `cargo-minimal-versions`, `cargo-no-dev-deps`, `martin`, `parse-changelog`, `parse-dockerfile`, `prek`, `uv`, `wasmtime`, `zizmor`, and `zola`. ([#1606](https://github.com/taiki-e/install-action/pull/1606))
@@ -5983,7 +6007,10 @@ Note: This release is considered a breaking change because installing on version
Initial release Initial release
[Unreleased]: https://github.com/taiki-e/install-action/compare/v2.69.4...HEAD [Unreleased]: https://github.com/taiki-e/install-action/compare/v2.69.7...HEAD
[2.69.7]: https://github.com/taiki-e/install-action/compare/v2.69.6...v2.69.7
[2.69.6]: https://github.com/taiki-e/install-action/compare/v2.69.5...v2.69.6
[2.69.5]: https://github.com/taiki-e/install-action/compare/v2.69.4...v2.69.5
[2.69.4]: https://github.com/taiki-e/install-action/compare/v2.69.3...v2.69.4 [2.69.4]: https://github.com/taiki-e/install-action/compare/v2.69.3...v2.69.4
[2.69.3]: https://github.com/taiki-e/install-action/compare/v2.69.2...v2.69.3 [2.69.3]: https://github.com/taiki-e/install-action/compare/v2.69.2...v2.69.3
[2.69.2]: https://github.com/taiki-e/install-action/compare/v2.69.1...v2.69.2 [2.69.2]: https://github.com/taiki-e/install-action/compare/v2.69.1...v2.69.2

View File

@@ -56,7 +56,7 @@ download_and_checksum() {
checksum='' checksum=''
fi fi
info "downloading ${url}" info "downloading ${url}"
retry curl --proto '=https' --tlsv1.2 -fsSL --retry 10 "${url}" -o tmp retry curl --proto '=https' --tlsv1.2 -fsSL "${url}" -o tmp
if [[ -n "${checksum}" ]]; then if [[ -n "${checksum}" ]]; then
info "verifying sha256 checksum for $(basename -- "${url}")" info "verifying sha256 checksum for $(basename -- "${url}")"
if type -P sha256sum >/dev/null; then if type -P sha256sum >/dev/null; then
@@ -233,7 +233,7 @@ read_manifest() {
# TODO: don't hardcode tool name and use 'immediate_yank_reflection' field in base manifest. # TODO: don't hardcode tool name and use 'immediate_yank_reflection' field in base manifest.
case "${tool}" in case "${tool}" in
cargo-nextest) cargo-nextest)
crate_info=$(curl -v --user-agent "${ACTION_USER_AGENT}" --proto '=https' --tlsv1.2 -fsSL --retry 10 "https://crates.io/api/v1/crates/${rust_crate}" || true) crate_info=$(retry curl --user-agent "${ACTION_USER_AGENT}" --proto '=https' --tlsv1.2 -fsSL "https://crates.io/api/v1/crates/${rust_crate}" || true)
if [[ -n "${crate_info}" ]]; then if [[ -n "${crate_info}" ]]; then
while true; do while true; do
yanked=$(jq -r ".versions[] | select(.num == \"${exact_version}\") | .yanked" <<<"${crate_info}") yanked=$(jq -r ".versions[] | select(.num == \"${exact_version}\") | .yanked" <<<"${crate_info}")

View File

@@ -22,32 +22,32 @@
}, },
"license_markdown": "[GPL-3.0](https://github.com/cargo-bins/cargo-binstall/blob/HEAD/crates/bin/LICENSE)", "license_markdown": "[GPL-3.0](https://github.com/cargo-bins/cargo-binstall/blob/HEAD/crates/bin/LICENSE)",
"latest": { "latest": {
"version": "1.17.7" "version": "1.17.8"
}, },
"1.17.7": { "1.17.8": {
"x86_64_linux_musl": { "x86_64_linux_musl": {
"etag": "0x8DE7C5AC0E4497E", "etag": "0x8DE8715DB8A1417",
"hash": "29b5ecfb6e03c2511a617c77d312b06df0c54717644fbfda3d465ec8240532f0" "hash": "1da1ef72448db667cc4ae6d48e37451087602c8c07dc61782a4a5e538303e015"
}, },
"x86_64_macos": { "x86_64_macos": {
"etag": "0x8DE7C5AC5E5BC1B", "etag": "0x8DE8715E03D9720",
"hash": "aa7174fb938e668dea4b4c3d22fe6cefed97642cc3a7a419ba96d63d63fd729b" "hash": "db353e01b582c97382178db9b4dfe22d81109782e480a38f3db953e62f569952"
}, },
"x86_64_windows": { "x86_64_windows": {
"etag": "0x8DE7C5AC441ACDB", "etag": "0x8DE8715DEAA171B",
"hash": "c5cb2444ee04480502a8ac73d96abd9f97af8300ec04ea1c1f2a9e959c02e4d6" "hash": "fef07560d4e391812091bb30c6ed1bd5289f74403a0c947b47b8a8c7a597b51b"
}, },
"aarch64_linux_musl": { "aarch64_linux_musl": {
"etag": "0x8DE7C5ACC95E091", "etag": "0x8DE8715E6784BD0",
"hash": "b0658b0a7f0959bc1dbb4ab665931c31c7dd1109ff01cb8772af17dfdc52a9af" "hash": "81d6245bd1a7a89e914d29af81d82280540e94927e61492a0fc359820cd97abb"
}, },
"aarch64_macos": { "aarch64_macos": {
"etag": "0x8DE7C5AD2545078", "etag": "0x8DE8715EBEC4A3F",
"hash": "1ad3c0c56fa3970634cce5009ed0ce61b943515f9115f8e480fd0e41d8d89085" "hash": "af87346fdb186f0a2333bc0a30cfddd6faa98b31145ef1bb19c284aedea65972"
}, },
"aarch64_windows": { "aarch64_windows": {
"etag": "0x8DE7C5AD068DA1E", "etag": "0x8DE8715EA179DDA",
"hash": "e876543c9aad23968d1123c0d959309937894bbfd267bb0878109fb253217878" "hash": "2270a5a7a8b3e85bd5fe32ac3fbd48cfd32d6f468a8c35499af8b65b806d271d"
} }
} }
} }

View File

@@ -28,10 +28,45 @@
}, },
"license_markdown": "[Apache-2.0](https://github.com/nextest-rs/nextest/blob/main/LICENSE-APACHE) OR [MIT](https://github.com/nextest-rs/nextest/blob/main/LICENSE-MIT)", "license_markdown": "[Apache-2.0](https://github.com/nextest-rs/nextest/blob/main/LICENSE-APACHE) OR [MIT](https://github.com/nextest-rs/nextest/blob/main/LICENSE-MIT)",
"latest": { "latest": {
"version": "0.9.131" "version": "0.9.132"
}, },
"0.9": { "0.9": {
"version": "0.9.131" "version": "0.9.132"
},
"0.9.132": {
"previous_stable_version": "0.9.131",
"x86_64_linux_gnu": {
"etag": "0x8DE86DAD99145E2",
"hash": "e22f14ecaff5519dbfe521e8717d64e9989648bddc23eb1f71bb0053518a52e7"
},
"x86_64_linux_musl": {
"etag": "0x8DE86DB277E6673",
"hash": "5e93f3b4244e2f8bffe5818b2a7cbd6a59f32d262dcb6017905e345cc74227af"
},
"x86_64_macos": {
"etag": "0x8DE86DB84D52D01",
"hash": "6ce5c844ae3cdac3f6f42fd86bf71a8bf99aecd76bab9f6743c808223d31fad1"
},
"x86_64_windows": {
"etag": "0x8DE86DBA597EDA0",
"hash": "6f934574f621613d7d759547401a04619b3e1ebe1f4e8f624880197d566fa6ad"
},
"aarch64_linux_gnu": {
"etag": "0x8DE86DBC5227BB5",
"hash": "592db3fa3d3ee62f109dc149554811eb8ecdc68e1514be5af79986b1560e2e0d"
},
"aarch64_linux_musl": {
"etag": "0x8DE86DAF803192A",
"hash": "2497ddfd2a0c6805f7c5514ff680c6ca9c1ffc5b9ab30af43e85830b768c129a"
},
"aarch64_windows": {
"etag": "0x8DE86DBA23732E6",
"hash": "3c519d128909fba6829a318476bcbd9777d97ec066b4d973a3f1e04da049ef0b"
},
"riscv64_linux_gnu": {
"etag": "0x8DE86DAD1A09377",
"hash": "82d1f0c4b7733e0a852e2e8b3f1a3f65b89e7ffd165121127ac356f0c9f7b5d2"
}
}, },
"0.9.131": { "0.9.131": {
"previous_stable_version": "0.9.130", "previous_stable_version": "0.9.130",

View File

@@ -13,13 +13,27 @@
}, },
"license_markdown": "[MIT](https://github.com/jfrimmel/cargo-valgrind/blob/master/LICENSE-MIT) OR [Apache-2.0](https://github.com/jfrimmel/cargo-valgrind/blob/master/LICENSE-APACHE)", "license_markdown": "[MIT](https://github.com/jfrimmel/cargo-valgrind/blob/master/LICENSE-MIT) OR [Apache-2.0](https://github.com/jfrimmel/cargo-valgrind/blob/master/LICENSE-APACHE)",
"latest": { "latest": {
"version": "2.4.0" "version": "2.4.1"
}, },
"2": { "2": {
"version": "2.4.0" "version": "2.4.1"
}, },
"2.4": { "2.4": {
"version": "2.4.0" "version": "2.4.1"
},
"2.4.1": {
"x86_64_linux_musl": {
"etag": "0x8DE88031FA217E4",
"hash": "85f0aba2bdde0d30bafa814a02229a56fd63b27623c25dfba57f49c40eeda762"
},
"x86_64_macos": {
"etag": "0x8DE88031F98D375",
"hash": "80b1477c1eb35410b40b842aa655b37233416f9cad86f6a11c6107b8b97ddc22"
},
"x86_64_windows": {
"etag": "0x8DE88031F98D375",
"hash": "c9f36f03de6ae0cb773b3796f4dd738ebb85e00a59c1cb72bec49a267d83c5cf"
}
}, },
"2.4.0": { "2.4.0": {
"x86_64_linux_musl": { "x86_64_linux_musl": {

36
manifests/knope.json generated
View File

@@ -3,10 +3,42 @@
"template": null, "template": null,
"license_markdown": "[MIT](https://github.com/knope-dev/knope/blob/main/LICENSE)", "license_markdown": "[MIT](https://github.com/knope-dev/knope/blob/main/LICENSE)",
"latest": { "latest": {
"version": "0.22.3" "version": "0.22.4"
}, },
"0.22": { "0.22": {
"version": "0.22.3" "version": "0.22.4"
},
"0.22.4": {
"x86_64_linux_musl": {
"url": "https://github.com/knope-dev/knope/releases/download/knope/v0.22.4/knope-x86_64-unknown-linux-musl.tgz",
"etag": "0x8DE8761D8F513DE",
"hash": "45a74925ae9f4c9c2c33b51992ae50241ec4fa836bf8d2977c0b8e8172dd69cf",
"bin": "knope-x86_64-unknown-linux-musl/knope"
},
"x86_64_macos": {
"url": "https://github.com/knope-dev/knope/releases/download/knope/v0.22.4/knope-x86_64-apple-darwin.tgz",
"etag": "0x8DE8761D8E4D27D",
"hash": "010dc197bf159bbd9d60e897252248ba2b0e204beae7250ce54a9deae1ec4876",
"bin": "knope-x86_64-apple-darwin/knope"
},
"x86_64_windows": {
"url": "https://github.com/knope-dev/knope/releases/download/knope/v0.22.4/knope-x86_64-pc-windows-msvc.tgz",
"etag": "0x8DE8761D8EAE61C",
"hash": "09f735b2da42cd594189042d1379c0a3a350a8c0ccb741015a84c6ff334543b1",
"bin": "knope-x86_64-pc-windows-msvc/knope.exe"
},
"aarch64_linux_musl": {
"url": "https://github.com/knope-dev/knope/releases/download/knope/v0.22.4/knope-aarch64-unknown-linux-musl.tgz",
"etag": "0x8DE8761D8EE649C",
"hash": "95e882afdb4154c5baaba91f7bbd1fb1d41cec6898363a2b30e7abad4057b83b",
"bin": "knope-aarch64-unknown-linux-musl/knope"
},
"aarch64_macos": {
"url": "https://github.com/knope-dev/knope/releases/download/knope/v0.22.4/knope-aarch64-apple-darwin.tgz",
"etag": "0x8DE8761D8EC1D47",
"hash": "02131f284315c8ece8a4ef69a0aff5f658309d4df73b95cfdfbe0fbd9e9ce259",
"bin": "knope-aarch64-apple-darwin/knope"
}
}, },
"0.22.3": { "0.22.3": {
"x86_64_linux_musl": { "x86_64_linux_musl": {

58
manifests/mise.json generated
View File

@@ -28,13 +28,65 @@
}, },
"license_markdown": "[MIT](https://github.com/jdx/mise/blob/main/LICENSE)", "license_markdown": "[MIT](https://github.com/jdx/mise/blob/main/LICENSE)",
"latest": { "latest": {
"version": "2026.3.9" "version": "2026.3.12"
}, },
"2026": { "2026": {
"version": "2026.3.9" "version": "2026.3.12"
}, },
"2026.3": { "2026.3": {
"version": "2026.3.9" "version": "2026.3.12"
},
"2026.3.12": {
"x86_64_linux_musl": {
"etag": "0x8DE886122F1FBF4",
"hash": "b6bdc23b08bccfcc77b61d35b286e20de1e0192447e8fda64b2bf60c5328b452"
},
"x86_64_macos": {
"etag": "0x8DE88612512C4AA",
"hash": "4f65f7f72c14641ad78670ec2c403d3eb9a4681e2fa064385e80a22a109bfe2c"
},
"x86_64_windows": {
"etag": "0x8DE886125F154A2",
"hash": "528d5f9bfabb8eae06964c0e112f9711a4af4315bfed96f6f50b6c4b67ec6f37"
},
"aarch64_linux_musl": {
"etag": "0x8DE88611FFB72E8",
"hash": "c27dd6a06053f6884c3f46763b64ef9c5e95fac7a9af89edea087b13cc68c521"
},
"aarch64_macos": {
"etag": "0x8DE88612449EE2B",
"hash": "c9622be7ea2badc7bf3501cd9b5600e0bf993bf33c162844a4a6d79a2209e1f8"
},
"aarch64_windows": {
"etag": "0x8DE8861257AFC36",
"hash": "a65bea150a477f21faa62e24d45f811d2ede69da20aac43ad17ef3134bc516f4"
}
},
"2026.3.10": {
"x86_64_linux_musl": {
"etag": "0x8DE8749F9B8C65D",
"hash": "b0c2fd25fe95cd1ed3f178b95690608472aa8a27ce2f6e63eaebda52a238e570"
},
"x86_64_macos": {
"etag": "0x8DE8749FBD637A3",
"hash": "5ed1a2a6a79aab33e67d21156ec42b22d3cde1ceef09eb08c0ccd9b429795e6a"
},
"x86_64_windows": {
"etag": "0x8DE8749FC98ACBC",
"hash": "bf5e86077f652caca0413155e33886c3459d3f2963f9f186be76c8c05c2accb6"
},
"aarch64_linux_musl": {
"etag": "0x8DE8749F6470833",
"hash": "9730abf52c93c7945f907f4fe6f731b79d74671705656fa36fa45008933e88c7"
},
"aarch64_macos": {
"etag": "0x8DE8749FB48C7AB",
"hash": "85b5e577a5ed34431718091122ea7ec9cf7d4e1d8e5e4dc298cdb02d8dbd97b3"
},
"aarch64_windows": {
"etag": "0x8DE8749FC8C37A5",
"hash": "82a702481c9e877b28f82eb60a0d3be2d393fc9b7915283992b1cd0263724d2b"
}
}, },
"2026.3.9": { "2026.3.9": {
"x86_64_linux_musl": { "x86_64_linux_musl": {

38
manifests/prek.json generated
View File

@@ -40,10 +40,44 @@
}, },
"license_markdown": "[MIT](https://github.com/j178/prek/blob/master/LICENSE)", "license_markdown": "[MIT](https://github.com/j178/prek/blob/master/LICENSE)",
"latest": { "latest": {
"version": "0.3.6" "version": "0.3.8"
}, },
"0.3": { "0.3": {
"version": "0.3.6" "version": "0.3.8"
},
"0.3.8": {
"x86_64_linux_musl": {
"etag": "0x8DE88B58DEC3E03",
"hash": "732cfb03960e6dfd5df2cb67906797aa8831750ef3a6f6340ec2b90ee8b7a59f"
},
"x86_64_macos": {
"etag": "0x8DE88B58D79B1EF",
"hash": "010198daf4e99a76d03a911973320542ffd7a04091cf7e86c60ac861187577f6"
},
"x86_64_windows": {
"etag": "0x8DE88B58DA4B02B",
"hash": "1da2735c31548dacd2751f90c15b5f643aa72f3053f366636f95153ee2c7186e"
},
"aarch64_linux_musl": {
"etag": "0x8DE88B58C3D8D01",
"hash": "b88d96aef4ea84999d12958c390611d3a26194ee57225fe2ff15a9855e3a71bf"
},
"aarch64_macos": {
"etag": "0x8DE88B58BFA193A",
"hash": "702fde4399fafb054ce85d0a64367689e3668b6475d732c4a46aae50cec0a4be"
},
"aarch64_windows": {
"etag": "0x8DE88B58BF4A12E",
"hash": "5ceb73fe9e18a987a498ced9b98a183d4110e3fdb534070d2d9911f860319e9f"
},
"riscv64_linux_gnu": {
"etag": "0x8DE88B58D4F0179",
"hash": "4c3677090f90f325b1f472e8b2db6f56647db794333ab0ee2656a91139ce9a27"
},
"s390x_linux_gnu": {
"etag": "0x8DE88B58D875E2B",
"hash": "cec915a22e4385110e2de8498d14c199ce6936d03d9d8340d166c57071ac1de8"
}
}, },
"0.3.6": { "0.3.6": {
"x86_64_linux_musl": { "x86_64_linux_musl": {

30
manifests/tombi.json generated
View File

@@ -22,10 +22,36 @@
}, },
"license_markdown": "[MIT](https://github.com/tombi-toml/tombi/blob/main/LICENSE)", "license_markdown": "[MIT](https://github.com/tombi-toml/tombi/blob/main/LICENSE)",
"latest": { "latest": {
"version": "0.9.8" "version": "0.9.9"
}, },
"0.9": { "0.9": {
"version": "0.9.8" "version": "0.9.9"
},
"0.9.9": {
"x86_64_linux_musl": {
"etag": "0x8DE86ED14380D61",
"hash": "4317ffc3e08fc6e3ba92953af85b6ad361493fd79916949aec5b5b7af471922f"
},
"x86_64_macos": {
"etag": "0x8DE86ED1495C95E",
"hash": "7f43d90e17f0a6406b5c53bb2773ea1f51e9aba00d47e76d6a55f70ba6e519e0"
},
"x86_64_windows": {
"etag": "0x8DE86ED1433F34E",
"hash": "efb2e3bd949435d244368179476658b5bed9bf35b662face2c00920faf97f247"
},
"aarch64_linux_musl": {
"etag": "0x8DE86ED1432BC2D",
"hash": "75d32d6d17a35c5307f7b67c4f80eca8d43b4d5bdacf86505a4bb87dcb3100d0"
},
"aarch64_macos": {
"etag": "0x8DE86ED149A3137",
"hash": "4e49e41a2f3bd7a26e11c0ce936ac0b75d60c9068798b951ea925cea04ce4a5b"
},
"aarch64_windows": {
"etag": "0x8DE86ED14385B2B",
"hash": "138047f5a495629a1fe60734706ab1d89fc8ed68eeab3dbd0b1865eab108f7b6"
}
}, },
"0.9.8": { "0.9.8": {
"x86_64_linux_musl": { "x86_64_linux_musl": {

View File

@@ -4,6 +4,9 @@
"rust_crate": "${package}", "rust_crate": "${package}",
"bin": "mise/bin/${package}${exe}", "bin": "mise/bin/${package}${exe}",
"version_range": ">= 2025.9.7", "version_range": ">= 2025.9.7",
"signing": {
"kind": "custom"
},
"platform": { "platform": {
"x86_64_linux_musl": { "x86_64_linux_musl": {
"asset_name": "${package}-v${version}-${rust_target_os}-x64-musl.tar.gz" "asset_name": "${package}-v${version}-${rust_target_os}-x64-musl.tar.gz"

View File

@@ -3,6 +3,10 @@
"tag_prefix": "v", "tag_prefix": "v",
"bin": "${package}${exe}", "bin": "${package}${exe}",
"version_range": ">= 0.83.0", "version_range": ">= 0.83.0",
"signing": {
"version_range": ">= 0.104.0",
"kind": "custom"
},
"platform": { "platform": {
"x86_64_linux_musl": { "x86_64_linux_musl": {
"asset_name": "${package}_${version}_linux_amd64.tar.gz" "asset_name": "${package}_${version}_linux_amd64.tar.gz"

View File

@@ -61,6 +61,18 @@ impl BaseManifest {
if self.platform.is_empty() { if self.platform.is_empty() {
panic!("At least one platform must be specified"); panic!("At least one platform must be specified");
} }
if let Some(website) = &self.website {
if website.is_empty() || *website == self.repository {
panic!(
"Please do not put the repository in website, or set website to an empty value"
);
}
}
if let Some(license_markdown) = &self.license_markdown {
if license_markdown.is_empty() {
panic!("license_markdown can not be an empty value");
}
}
} }
} }
@@ -83,6 +95,8 @@ pub enum SigningKind {
/// public key: package.metadata.binstall.signing.pubkey at Cargo.toml /// public key: package.metadata.binstall.signing.pubkey at Cargo.toml
/// <https://github.com/cargo-bins/cargo-binstall/blob/HEAD/SIGNING.md> /// <https://github.com/cargo-bins/cargo-binstall/blob/HEAD/SIGNING.md>
MinisignBinstall, MinisignBinstall,
/// tool-specific
Custom,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]

View File

@@ -9,7 +9,7 @@ use std::{
env, env,
ffi::OsStr, ffi::OsStr,
io::Read as _, io::Read as _,
path::Path, path::{Path, PathBuf},
sync::{LazyLock, RwLock}, sync::{LazyLock, RwLock},
time::Duration, time::Duration,
}; };
@@ -20,9 +20,10 @@ use install_action_internal_codegen::{
BaseManifest, HostPlatform, Manifest, ManifestDownloadInfo, ManifestRef, ManifestTemplate, BaseManifest, HostPlatform, Manifest, ManifestDownloadInfo, ManifestRef, ManifestTemplate,
ManifestTemplateDownloadInfo, Manifests, SigningKind, Version, workspace_root, ManifestTemplateDownloadInfo, Manifests, SigningKind, Version, workspace_root,
}; };
use serde::de::DeserializeOwned;
use spdx::expression::{ExprNode, ExpressionReq, Operator}; use spdx::expression::{ExprNode, ExpressionReq, Operator};
fn main() -> Result<()> { fn main() {
let args: Vec<_> = env::args().skip(1).collect(); let args: Vec<_> = env::args().skip(1).collect();
if args.is_empty() || args.iter().any(|arg| arg.starts_with('-')) { if args.is_empty() || args.iter().any(|arg| arg.starts_with('-')) {
println!( println!(
@@ -30,7 +31,7 @@ fn main() -> Result<()> {
); );
std::process::exit(1); std::process::exit(1);
} }
let package = &args[0]; let package = &*args[0];
let version_req = args.get(1); let version_req = args.get(1);
let version_req_given = version_req.is_some(); let version_req_given = version_req.is_some();
let skip_existing_manifest_versions = std::env::var("SKIP_EXISTING_MANIFEST_VERSIONS").is_ok(); let skip_existing_manifest_versions = std::env::var("SKIP_EXISTING_MANIFEST_VERSIONS").is_ok();
@@ -38,23 +39,25 @@ fn main() -> Result<()> {
let workspace_root = workspace_root(); let workspace_root = workspace_root();
let manifest_path = &workspace_root.join("manifests").join(format!("{package}.json")); let manifest_path = &workspace_root.join("manifests").join(format!("{package}.json"));
let download_cache_dir = &workspace_root.join("tools/codegen/tmp/cache").join(package); let download_cache_dir = &workspace_root.join("tools/codegen/tmp/cache").join(package);
fs::create_dir_all(manifest_path.parent().unwrap())?; fs::create_dir_all(manifest_path.parent().unwrap()).unwrap();
fs::create_dir_all(download_cache_dir)?; fs::create_dir_all(download_cache_dir).unwrap();
eprintln!("download cache: {}", download_cache_dir.display()); eprintln!("download cache: {}", download_cache_dir.display());
let mut base_info: BaseManifest = serde_json::from_slice(&fs::read( let mut base_info: BaseManifest = serde_json::from_slice(
workspace_root.join("tools/codegen/base").join(format!("{package}.json")), &fs::read(workspace_root.join("tools/codegen/base").join(format!("{package}.json")))
)?)?; .unwrap(),
)
.unwrap();
base_info.validate(); base_info.validate();
let repo = base_info let repo = base_info
.repository .repository
.strip_prefix("https://github.com/") .strip_prefix("https://github.com/")
.context("repository must start with https://github.com/")?; .context("repository must start with https://github.com/")
.unwrap();
eprintln!("downloading metadata from {GITHUB_API_START}repos/{repo}"); eprintln!("downloading metadata from {GITHUB_API_START}repos/{repo}");
let repo_info: github::RepoMetadata = let repo_info: github::RepoMetadata = download_json(&format!("{GITHUB_API_START}repos/{repo}"));
download(&format!("{GITHUB_API_START}repos/{repo}"))?.into_json()?;
eprintln!("downloading releases from {GITHUB_API_START}repos/{repo}/releases"); eprintln!("downloading releases from {GITHUB_API_START}repos/{repo}/releases");
let mut releases: github::Releases = vec![]; let mut releases: github::Releases = vec![];
@@ -62,10 +65,9 @@ fn main() -> Result<()> {
// is greater than 100, multiple fetches are needed. // is greater than 100, multiple fetches are needed.
for page in 1.. { for page in 1.. {
let per_page = 100; let per_page = 100;
let mut r: github::Releases = download(&format!( let mut r: github::Releases = download_json(&format!(
"{GITHUB_API_START}repos/{repo}/releases?per_page={per_page}&page={page}" "{GITHUB_API_START}repos/{repo}/releases?per_page={per_page}&page={page}"
))? ));
.into_json()?;
// If version_req is latest, it is usually sufficient to look at the latest 100 releases. // If version_req is latest, it is usually sufficient to look at the latest 100 releases.
if r.len() < per_page || version_req.is_some_and(|req| req == "latest") { if r.len() < per_page || version_req.is_some_and(|req| req == "latest") {
releases.append(&mut r); releases.append(&mut r);
@@ -103,23 +105,25 @@ fn main() -> Result<()> {
.rust_crate .rust_crate
.as_ref() .as_ref()
.map(|s| replace_vars(s, package, None, None, base_info.rust_crate.as_deref())) .map(|s| replace_vars(s, package, None, None, base_info.rust_crate.as_deref()))
.transpose()?; .transpose()
.unwrap();
if let Some(crate_name) = &base_info.rust_crate { if let Some(crate_name) = &base_info.rust_crate {
eprintln!("downloading crate info from https://crates.io/api/v1/crates/{crate_name}"); eprintln!("downloading crate info from https://crates.io/api/v1/crates/{crate_name}");
let info = download(&format!("https://crates.io/api/v1/crates/{crate_name}"))? let info: crates_io::Crate =
.into_json::<crates_io::Crate>()?; download_json(&format!("https://crates.io/api/v1/crates/{crate_name}"));
let latest_version = &info.versions[0].num; let latest_version = &info.versions[0].num;
crates_io_version_detail = Some( crates_io_version_detail = Some(
download(&format!("https://crates.io/api/v1/crates/{crate_name}/{latest_version}"))? download_json::<crates_io::VersionMetadata>(&format!(
.into_json::<crates_io::VersionMetadata>()? "https://crates.io/api/v1/crates/{crate_name}/{latest_version}"
.version, ))
.version,
); );
if let Some(crate_repository) = info.crate_.repository.clone() { if let Some(crate_repository) = info.crate_.repository.clone() {
if !crate_repository.to_lowercase().starts_with(&base_info.repository.to_lowercase()) { if !crate_repository.to_lowercase().starts_with(&base_info.repository.to_lowercase()) {
panic!("repository {crate_repository} from crates.io differs from base manifest"); panic!("repository {crate_repository} from crates.io differs from base manifest");
} }
} else if crate_name != "zola" { } else {
panic!("crate metadata does not include a repository"); panic!("crate metadata does not include a repository");
} }
@@ -139,7 +143,7 @@ fn main() -> Result<()> {
if manifest_path.is_file() { if manifest_path.is_file() {
println!("loading pre-existing manifest {}", manifest_path.display()); println!("loading pre-existing manifest {}", manifest_path.display());
match serde_json::from_slice(&fs::read(manifest_path)?) { match serde_json::from_slice(&fs::read(manifest_path).unwrap()) {
Ok(m) => { Ok(m) => {
manifests = m; manifests = m;
for (k, manifest) in &mut manifests.map { for (k, manifest) in &mut manifests.map {
@@ -165,18 +169,8 @@ fn main() -> Result<()> {
} }
} }
// Check website
if let Some(website) = base_info.website {
if website.is_empty() || website == base_info.repository {
panic!("Please do not put the repository in website, or set website to an empty value");
}
}
// Populate license_markdown from the base manifest if present. // Populate license_markdown from the base manifest if present.
if let Some(license_markdown) = base_info.license_markdown { if let Some(license_markdown) = base_info.license_markdown {
if license_markdown.is_empty() {
panic!("license_markdown can not be an empty value");
}
manifests.license_markdown = license_markdown; manifests.license_markdown = license_markdown;
} }
@@ -184,7 +178,7 @@ fn main() -> Result<()> {
if !manifests.license_markdown.is_empty() { if !manifests.license_markdown.is_empty() {
let urls = get_license_markdown_urls(&manifests.license_markdown); let urls = get_license_markdown_urls(&manifests.license_markdown);
if urls.is_empty() { if urls.is_empty() {
bail!("Could not find URLs in license_markdown: {}.", manifests.license_markdown); panic!("Could not find URLs in license_markdown: {}.", manifests.license_markdown);
} }
for url in urls { for url in urls {
if let Err(err) = github_head(&url) { if let Err(err) = github_head(&url) {
@@ -207,7 +201,7 @@ fn main() -> Result<()> {
spdx_id spdx_id
} }
_ => { _ => {
bail!( panic!(
"No license SPDX found in crates.io or GitHub metadata.\n\ "No license SPDX found in crates.io or GitHub metadata.\n\
Please set license_markdown in the base manifest" Please set license_markdown in the base manifest"
); );
@@ -218,7 +212,7 @@ fn main() -> Result<()> {
{ {
manifests.license_markdown = license_markdown; manifests.license_markdown = license_markdown;
} else { } else {
bail!( panic!(
"Unable to verify license file(s) in the repo for license {license}.\n\ "Unable to verify license file(s) in the repo for license {license}.\n\
Please set license_markdown in the base manifest" Please set license_markdown in the base manifest"
); );
@@ -227,13 +221,13 @@ fn main() -> Result<()> {
let version_req: semver::VersionReq = match version_req { let version_req: semver::VersionReq = match version_req {
_ if latest_only => { _ if latest_only => {
let req = format!("={}", releases.first_key_value().unwrap().0.0).parse()?; let req = format!("={}", releases.first_key_value().unwrap().0.0).parse().unwrap();
eprintln!("update manifest for versions '{req}'"); eprintln!("update manifest for versions '{req}'");
req req
} }
None => match base_info.version_range { None => match base_info.version_range {
Some(version_range) => version_range.parse()?, Some(version_range) => version_range.parse().unwrap(),
None => ">= 0.0.1".parse()?, // HACK: ignore pre-releases None => ">= 0.0.1".parse().unwrap(), // HACK: ignore pre-releases
}, },
Some(version_req) => { Some(version_req) => {
for version in manifests.map.keys() { for version in manifests.map.keys() {
@@ -249,18 +243,26 @@ fn main() -> Result<()> {
let req = if version_req == "latest" { let req = if version_req == "latest" {
// TODO: this should check all missing versions // TODO: this should check all missing versions
if manifests.map.is_empty() { if manifests.map.is_empty() {
format!("={}", releases.first_key_value().unwrap().0.0).parse()? format!("={}", releases.first_key_value().unwrap().0.0).parse().unwrap()
} else { } else {
format!(">={}", semver_versions.last().unwrap()).parse()? format!(">={}", semver_versions.last().unwrap()).parse().unwrap()
} }
} else { } else {
version_req.parse()? version_req.parse().unwrap()
}; };
eprintln!("update manifest for versions '{req}'"); eprintln!("update manifest for versions '{req}'");
req req
} }
}; };
let signing_version_req: Option<semver::VersionReq> =
base_info.signing.as_ref().map(|signing| {
match &signing.version_range {
Some(version_range) => version_range.parse().unwrap(),
None => ">= 0.0.1".parse().unwrap(), // HACK: ignore pre-releases
}
});
let mut buf = vec![]; let mut buf = vec![];
let mut buf2 = vec![]; let mut buf2 = vec![];
for (Reverse(semver_version), (version, release)) in &releases { for (Reverse(semver_version), (version, release)) in &releases {
@@ -282,11 +284,154 @@ fn main() -> Result<()> {
continue; continue;
} }
let signing_version_req: Option<semver::VersionReq> = match &base_info.signing { let mut verified_checksum: Option<Vec<_>> = None;
match &base_info.signing {
Some(signing) => { Some(signing) => {
match &signing.version_range { if let SigningKind::Custom = signing.kind {
Some(version_range) => Some(version_range.parse()?), match package {
None => Some(">= 0.0.1".parse()?), // HACK: ignore pre-releases _ if !signing_version_req.as_ref().unwrap().matches(semver_version) => {}
"mise" => {
// Refs: https://github.com/jdx/mise/blob/v2026.3.9/src/minisign.rs
let crates_io_info = crates_io_info.as_ref().unwrap();
let [checksum, sig] =
["SHASUMS256.txt", "SHASUMS256.txt.minisig"].map(|f| {
let Some(asset) =
release.assets.iter().find(|asset| asset.name == f)
else {
// There is broken release which has no release assets: https://github.com/jdx/mise/releases/tag/v2026.2.14
return PathBuf::new();
};
let download_cache =
download_cache_dir.join(format!("{version}-{f}"));
let url = &asset.browser_download_url;
eprint!("downloading {url} for signature verification ... ");
if download_cache.is_file() {
eprintln!("already downloaded");
} else {
download_to_buf(url, &mut buf);
eprintln!("download complete");
fs::write(&download_cache, &buf).unwrap();
buf.clear();
}
download_cache
});
if checksum.as_os_str().is_empty() || sig.as_os_str().is_empty() {
continue;
}
let v = crates_io_info
.versions
.iter()
.find(|v| v.num == *semver_version)
.unwrap();
let url = format!("https://crates.io{}", v.dl_path);
let pubkey_download_cache =
&download_cache_dir.join(format!("{version}-minisign.pub"));
eprint!("downloading {url} for signature verification ... ");
if pubkey_download_cache.is_file() {
eprintln!("already downloaded");
} else {
download_to_buf(&url, &mut buf);
let hash = ring::digest::digest(&ring::digest::SHA256, &buf);
if format!("{hash:?}").strip_prefix("SHA256:").unwrap()
!= v.checksum
{
panic!("checksum mismatch for {url}");
}
let decoder = flate2::read::GzDecoder::new(&*buf);
let mut archive = tar::Archive::new(decoder);
for entry in archive.entries().unwrap() {
let mut entry = entry.unwrap();
let path = entry.path().unwrap();
if path.file_name() == Some(OsStr::new("minisign.pub")) {
entry.unpack(pubkey_download_cache).unwrap();
break;
}
}
buf.clear();
eprintln!("download complete");
}
let pubkey =
minisign_verify::PublicKey::from_file(pubkey_download_cache)
.unwrap();
eprint!("verifying checksum file for {package}@{version} ... ");
let allow_legacy = false;
pubkey
.verify(
&fs::read(&checksum).unwrap(),
&minisign_verify::Signature::from_file(sig).unwrap(),
allow_legacy,
)
.unwrap();
verified_checksum = Some(
fs::read_to_string(checksum)
.unwrap()
.lines()
.filter_map(|l| l.split_once(" "))
.map(|(h, f)| {
(f.trim_ascii().to_owned(), h.trim_ascii().to_owned())
})
.collect(),
);
eprintln!("done");
}
"syft" => {
// Refs: https://oss.anchore.com/docs/installation/verification/
let [checksum, certificate, signature] =
["checksums.txt", "checksums.txt.pem", "checksums.txt.sig"].map(
|f| {
let asset = release
.assets
.iter()
.find(|asset| asset.name.ends_with(f))
.unwrap();
let download_cache =
download_cache_dir.join(format!("{version}-{f}"));
let url = &asset.browser_download_url;
eprint!(
"downloading {url} for signature verification ... "
);
if download_cache.is_file() {
eprintln!("already downloaded");
} else {
download_to_buf(url, &mut buf);
eprintln!("download complete");
fs::write(&download_cache, &buf).unwrap();
buf.clear();
}
download_cache
},
);
eprint!("verifying checksum file for {package}@{version} ... ");
cmd!(
"cosign",
"verify-blob",
&checksum,
"--certificate",
certificate,
"--signature",
signature,
"--certificate-identity-regexp",
format!("https://github\\.com/{repo}/\\.github/workflows/.+"),
"--certificate-oidc-issuer",
"https://token.actions.githubusercontent.com"
)
.run()
.unwrap();
verified_checksum = Some(
fs::read_to_string(checksum)
.unwrap()
.lines()
.filter_map(|l| l.split_once(" "))
.map(|(h, f)| {
(f.trim_ascii().to_owned(), h.trim_ascii().to_owned())
})
.collect(),
);
eprintln!("done");
}
_ => {}
}
} }
} }
None => { None => {
@@ -294,24 +439,29 @@ fn main() -> Result<()> {
asset.name.contains(".asc") asset.name.contains(".asc")
|| asset.name.contains(".gpg") || asset.name.contains(".gpg")
|| asset.name.contains(".sig") || asset.name.contains(".sig")
|| asset.name.contains(".minisig")
|| asset.name.contains(".pem")
|| asset.name.contains(".crt")
|| asset.name.contains(".key")
|| asset.name.contains(".pub")
}) { }) {
eprintln!( eprintln!(
"{package} may supports other signing verification method using {}", "{package} may supports other signature verification method using {}",
asset.name asset.name
); );
} }
None
} }
}; }
let mut download_info = BTreeMap::new(); let mut download_info = BTreeMap::new();
let mut pubkey = None; let mut minisign_binstall_pubkey = None;
for (&platform, base_download_info) in &base_info.platform { for (&platform, base_download_info) in &base_info.platform {
let asset_names = base_download_info let asset_names = base_download_info
.asset_name .asset_name
.as_ref() .as_ref()
.or(base_info.asset_name.as_ref()) .or(base_info.asset_name.as_ref())
.with_context(|| format!("asset_name is needed for {package} on {platform:?}"))? .with_context(|| format!("asset_name is needed for {package} on {platform:?}"))
.unwrap()
.as_slice() .as_slice()
.iter() .iter()
.map(|asset_name| { .map(|asset_name| {
@@ -323,7 +473,8 @@ fn main() -> Result<()> {
base_info.rust_crate.as_deref(), base_info.rust_crate.as_deref(),
) )
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()
.unwrap();
let (url, digest, asset_name) = match asset_names.iter().find_map(|asset_name| { let (url, digest, asset_name) = match asset_names.iter().find_map(|asset_name| {
release release
.assets .assets
@@ -345,7 +496,7 @@ fn main() -> Result<()> {
"{version}-{platform:?}-{}", "{version}-{platform:?}-{}",
Path::new(&url).file_name().unwrap().to_str().unwrap() Path::new(&url).file_name().unwrap().to_str().unwrap()
)); ));
let response = download(&url)?; let response = download(&url).unwrap();
let etag = let etag =
response.header("etag").expect("binary should have an etag").replace('\"', ""); response.header("etag").expect("binary should have an etag").replace('\"', "");
@@ -365,11 +516,11 @@ fn main() -> Result<()> {
if download_cache.is_file() { if download_cache.is_file() {
eprintln!("already downloaded"); eprintln!("already downloaded");
fs::File::open(download_cache)?.read_to_end(&mut buf)?; // Not buffered because it is read at once. fs::File::open(download_cache).unwrap().read_to_end(&mut buf).unwrap(); // Not buffered because it is read at once.
} else { } else {
response.into_reader().read_to_end(&mut buf)?; response.into_reader().read_to_end(&mut buf).unwrap();
eprintln!("download complete"); eprintln!("download complete");
fs::write(download_cache, &buf)?; fs::write(download_cache, &buf).unwrap();
} }
eprintln!("getting sha256 hash for {url}"); eprintln!("getting sha256 hash for {url}");
@@ -377,7 +528,7 @@ fn main() -> Result<()> {
let hash = format!("{hash:?}").strip_prefix("SHA256:").unwrap().to_owned(); let hash = format!("{hash:?}").strip_prefix("SHA256:").unwrap().to_owned();
if let Some(digest) = digest { if let Some(digest) = digest {
if hash != digest.strip_prefix("sha256:").unwrap() { if hash != digest.strip_prefix("sha256:").unwrap() {
bail!( panic!(
"digest mismatch between GitHub release page and actually downloaded file" "digest mismatch between GitHub release page and actually downloaded file"
); );
} }
@@ -401,9 +552,15 @@ fn main() -> Result<()> {
signer_workflow, signer_workflow,
&download_cache &download_cache
) )
.run()?; .run()
.unwrap();
} }
SigningKind::MinisignBinstall => { SigningKind::MinisignBinstall => {
let Some(crates_io_info) = &crates_io_info else {
panic!(
"signing kind minisign-binstall is supported only for rust crate"
);
};
let url = url.clone() + ".sig"; let url = url.clone() + ".sig";
let sig_download_cache = &download_cache.with_extension(format!( let sig_download_cache = &download_cache.with_extension(format!(
"{}.sig", "{}.sig",
@@ -412,19 +569,14 @@ fn main() -> Result<()> {
eprint!("downloading {url} for signature validation ... "); eprint!("downloading {url} for signature validation ... ");
let sig = if sig_download_cache.is_file() { let sig = if sig_download_cache.is_file() {
eprintln!("already downloaded"); eprintln!("already downloaded");
minisign_verify::Signature::from_file(sig_download_cache)? minisign_verify::Signature::from_file(sig_download_cache).unwrap()
} else { } else {
let buf = download(&url)?.into_string()?; let buf = download(&url).unwrap().into_string().unwrap();
eprintln!("download complete"); eprintln!("download complete");
fs::write(sig_download_cache, &buf)?; fs::write(sig_download_cache, &buf).unwrap();
minisign_verify::Signature::decode(&buf)? minisign_verify::Signature::decode(&buf).unwrap()
}; };
let Some(crates_io_info) = &crates_io_info else {
bail!(
"signing kind minisign-binstall is supported only for rust crate"
);
};
let v = crates_io_info let v = crates_io_info
.versions .versions
.iter() .iter()
@@ -437,28 +589,29 @@ fn main() -> Result<()> {
if crate_download_cache.is_file() { if crate_download_cache.is_file() {
eprintln!("already downloaded"); eprintln!("already downloaded");
} else { } else {
download(&url)?.into_reader().read_to_end(&mut buf2)?; download_to_buf(&url, &mut buf2);
let hash = ring::digest::digest(&ring::digest::SHA256, &buf2); let hash = ring::digest::digest(&ring::digest::SHA256, &buf2);
if format!("{hash:?}").strip_prefix("SHA256:").unwrap() != v.checksum { if format!("{hash:?}").strip_prefix("SHA256:").unwrap() != v.checksum {
bail!("checksum mismatch for {url}"); panic!("checksum mismatch for {url}");
} }
let decoder = flate2::read::GzDecoder::new(&*buf2); let decoder = flate2::read::GzDecoder::new(&*buf2);
let mut archive = tar::Archive::new(decoder); let mut archive = tar::Archive::new(decoder);
for entry in archive.entries()? { for entry in archive.entries().unwrap() {
let mut entry = entry?; let mut entry = entry.unwrap();
let path = entry.path()?; let path = entry.path().unwrap();
if path.file_name() == Some(OsStr::new("Cargo.toml")) { if path.file_name() == Some(OsStr::new("Cargo.toml")) {
entry.unpack(crate_download_cache)?; entry.unpack(crate_download_cache).unwrap();
break; break;
} }
} }
buf2.clear(); buf2.clear();
eprintln!("download complete"); eprintln!("download complete");
} }
if pubkey.is_none() { if minisign_binstall_pubkey.is_none() {
let cargo_manifest = toml::de::from_str::<cargo_manifest::Manifest>( let cargo_manifest = toml::de::from_str::<cargo_manifest::Manifest>(
&fs::read_to_string(crate_download_cache)?, &fs::read_to_string(crate_download_cache).unwrap(),
)?; )
.unwrap();
eprintln!( eprintln!(
"algorithm: {}", "algorithm: {}",
cargo_manifest.package.metadata.binstall.signing.algorithm cargo_manifest.package.metadata.binstall.signing.algorithm
@@ -471,16 +624,42 @@ fn main() -> Result<()> {
cargo_manifest.package.metadata.binstall.signing.algorithm, cargo_manifest.package.metadata.binstall.signing.algorithm,
"minisign" "minisign"
); );
pubkey = Some(minisign_verify::PublicKey::from_base64( minisign_binstall_pubkey = Some(
&cargo_manifest.package.metadata.binstall.signing.pubkey, minisign_verify::PublicKey::from_base64(
)?); &cargo_manifest.package.metadata.binstall.signing.pubkey,
)
.unwrap(),
);
} }
let pubkey = pubkey.as_ref().unwrap(); let pubkey = minisign_binstall_pubkey.as_ref().unwrap();
eprint!("verifying signature for {bin_url} ... "); eprint!("verifying signature for {bin_url} ... ");
let allow_legacy = false; let allow_legacy = false;
pubkey.verify(&buf, &sig, allow_legacy)?; pubkey.verify(&buf, &sig, allow_legacy).unwrap();
eprintln!("done"); eprintln!("done");
} }
SigningKind::Custom => {
if let Some(verified_checksum) = &verified_checksum {
let asset_name_cwd = format!("./{asset_name}");
let mut checked = false;
for (f, h) in verified_checksum {
if *f == asset_name || *f == asset_name_cwd {
checked = true;
assert_eq!(
hash, *h,
"verified checksum doesn't match with sha256 hash of {asset_name} in {package}@{version}"
);
}
}
assert!(
checked,
"{asset_name} not found in verified checksum for {package}@{version}"
);
} else {
unimplemented!(
"unimplemented tool-specific signing handling for {package}"
);
}
}
} }
} }
@@ -563,7 +742,7 @@ fn main() -> Result<()> {
// update an existing manifests.json to avoid discarding work done in the event of a fetch error. // update an existing manifests.json to avoid discarding work done in the event of a fetch error.
if existing_manifest.is_some() && !version_req_given { if existing_manifest.is_some() && !version_req_given {
write_manifests(manifest_path, &manifests.clone())?; write_manifests(manifest_path, &manifests.clone()).unwrap();
eprintln!("wrote {} with incomplete data", manifest_path.display()); eprintln!("wrote {} with incomplete data", manifest_path.display());
} }
} }
@@ -636,7 +815,7 @@ fn main() -> Result<()> {
.values() .values()
.any(|m| matches!(m, ManifestRef::Real(m) if m.download_info.contains_key(&p))) .any(|m| matches!(m, ManifestRef::Real(m) if m.download_info.contains_key(&p)))
{ {
bail!( panic!(
"platform list in base manifest for {package} contains {p:?}, \ "platform list in base manifest for {package} contains {p:?}, \
but result manifest doesn't contain it; \ but result manifest doesn't contain it; \
consider removing {p:?} from platform list in base manifest" consider removing {p:?} from platform list in base manifest"
@@ -680,7 +859,7 @@ fn main() -> Result<()> {
// until 2027-08, people aren't paying much attention to it at this time. // until 2027-08, people aren't paying much attention to it at this time.
continue; continue;
} }
bail!( panic!(
"platform list in base manifest for {package} contains {p:?}, \ "platform list in base manifest for {package} contains {p:?}, \
but latest release ({latest_version}) doesn't contain it; \ but latest release ({latest_version}) doesn't contain it; \
consider marking {latest_version} as broken by adding 'broken' field to base manifest" consider marking {latest_version} as broken by adding 'broken' field to base manifest"
@@ -720,10 +899,8 @@ fn main() -> Result<()> {
manifests.rust_crate = base_info.rust_crate; manifests.rust_crate = base_info.rust_crate;
write_manifests(manifest_path, &manifests)?; write_manifests(manifest_path, &manifests).unwrap();
eprintln!("wrote {}", manifest_path.display()); eprintln!("wrote {}", manifest_path.display());
Ok(())
} }
fn write_manifests(manifest_path: &Path, manifests: &Manifests) -> Result<()> { fn write_manifests(manifest_path: &Path, manifests: &Manifests) -> Result<()> {
@@ -858,6 +1035,16 @@ fn download(url: &str) -> Result<ureq::Response> {
Err(last_error.unwrap().into()) Err(last_error.unwrap().into())
} }
#[track_caller]
fn download_to_buf(url: &str, buf: &mut Vec<u8>) {
download(url).unwrap().into_reader().read_to_end(buf).unwrap();
}
#[track_caller]
fn download_json<T: DeserializeOwned>(url: &str) -> T {
download(url).unwrap().into_json().unwrap()
}
fn github_head(url: &str) -> Result<()> { fn github_head(url: &str) -> Result<()> {
eprintln!("fetching head of {url} .."); eprintln!("fetching head of {url} ..");
let mut token = GITHUB_TOKENS.get(url); let mut token = GITHUB_TOKENS.get(url);

View File

@@ -6,7 +6,6 @@ use std::{
path::PathBuf, path::PathBuf,
}; };
use anyhow::Result;
use fs_err as fs; use fs_err as fs;
use install_action_internal_codegen::{BaseManifest, Manifests, workspace_root}; use install_action_internal_codegen::{BaseManifest, Manifests, workspace_root};
@@ -32,7 +31,7 @@ const FOOTER: &str = "
[cargo-binstall]: https://github.com/cargo-bins/cargo-binstall [cargo-binstall]: https://github.com/cargo-bins/cargo-binstall
"; ";
fn main() -> Result<()> { fn main() {
let args: Vec<_> = env::args().skip(1).collect(); let args: Vec<_> = env::args().skip(1).collect();
if !args.is_empty() || args.iter().any(|arg| arg.starts_with('-')) { if !args.is_empty() || args.iter().any(|arg| arg.starts_with('-')) {
println!( println!(
@@ -72,9 +71,10 @@ fn main() -> Result<()> {
name.set_extension(""); name.set_extension("");
let name = name.to_string_lossy().to_string(); let name = name.to_string_lossy().to_string();
let base_info: BaseManifest = let base_info: BaseManifest =
serde_json::from_slice(&fs::read(base_info_dir.join(file_name.clone()))?)?; serde_json::from_slice(&fs::read(base_info_dir.join(file_name.clone())).unwrap())
.unwrap();
let manifests: Manifests = let manifests: Manifests =
serde_json::from_slice(&fs::read(manifest_dir.join(file_name))?)?; serde_json::from_slice(&fs::read(manifest_dir.join(file_name)).unwrap()).unwrap();
let website = match base_info.website { let website = match base_info.website {
Some(website) => website, Some(website) => website,
@@ -135,9 +135,7 @@ fn main() -> Result<()> {
} }
file.write_all(FOOTER.as_bytes()).expect("Unable to write footer"); file.write_all(FOOTER.as_bytes()).expect("Unable to write footer");
file.flush()?; file.flush().unwrap();
Ok(())
} }
#[derive(Debug)] #[derive(Debug)]

View File

@@ -11,6 +11,11 @@ cd -- "$(dirname -- "$0")"/..
# ./tools/manifest.sh [PACKAGE [VERSION_REQ]] # ./tools/manifest.sh [PACKAGE [VERSION_REQ]]
# ./tools/manifest.sh full # ./tools/manifest.sh full
if [[ -n "${GITHUB_ACTIONS:-}" ]] && ! type -P cosign; then
go install github.com/sigstore/cosign/v3/cmd/cosign@latest
sudo mv -- ~/go/bin/cosign /usr/local/bin
fi
if [[ $# -eq 1 ]] && [[ "$1" == "full" ]]; then if [[ $# -eq 1 ]] && [[ "$1" == "full" ]]; then
for manifest in tools/codegen/base/*.json; do for manifest in tools/codegen/base/*.json; do
package="${manifest##*/}" package="${manifest##*/}"