mirror of
https://github.com/taiki-e/install-action.git
synced 2026-04-08 18:05:42 +08:00
Support artifact attestations verification
This commit is contained in:
@@ -10,6 +10,10 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- 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))
|
||||
|
||||
- Update `biome@latest` to 2.4.8.
|
||||
|
||||
- Update `tombi@latest` to 0.9.8.
|
||||
|
||||
- Update `parse-dockerfile@latest` to 0.1.5.
|
||||
|
||||
@@ -104,7 +104,7 @@ When installing the tool from GitHub Releases, this action will download the too
|
||||
|
||||
Additionally, this action will also verify SHA256 checksums for downloaded files in all tools installed from GitHub Releases. This is enabled by default and can be disabled by setting the `checksum` input option to `false`.
|
||||
|
||||
Additionally, we also verify signature if the tool distributes signed archives. Signature verification is done at the stage of getting the checksum, so disabling the checksum will also disable signature verification.
|
||||
Additionally, we also verify [artifact attestations](https://docs.github.com/en/actions/concepts/security/artifact-attestations) or signature if the tool publishes artifact attestations or distributes signed archives. Verification is done at the stage of getting the checksum, so disabling the checksum will also disable verification.
|
||||
|
||||
See the linked documentation for information on security when installed using [snap](https://snapcraft.io/docs) or [cargo-binstall](https://github.com/cargo-bins/cargo-binstall#faq).
|
||||
|
||||
|
||||
2467
manifests/biome.json
generated
2467
manifests/biome.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,16 @@
|
||||
"repository": "https://github.com/biomejs/biome",
|
||||
"website": "https://biomejs.dev",
|
||||
"license_markdown": "[Apache-2.0](https://github.com/biomejs/biome/blob/main/LICENSE-APACHE) OR [MIT](https://github.com/biomejs/biome/blob/main/LICENSE-MIT)",
|
||||
"tag_prefix": "cli/v",
|
||||
"tag_prefix": ["@biomejs/biome@", "cli/v"],
|
||||
"bin": "${package}${exe}",
|
||||
"signing": {
|
||||
"version_range": ">= 2.3.9",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {
|
||||
"asset_name": "${package}-linux-x64"
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
"rust_crate": "${package}",
|
||||
"bin": "${package}-${rust_target}/${package}${exe}",
|
||||
"version_range": ">= 0.5.0",
|
||||
"signing": {
|
||||
"version_range": ">= 0.5.4",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {
|
||||
"asset_name": "${package}-linux-amd64.tar.gz"
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
"${package}-${rust_target}.zip",
|
||||
"${package}-v${version}-${rust_target}.zip"
|
||||
],
|
||||
"signing": {
|
||||
"version_range": ">= 0.6.44",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "taiki-e/github-actions/.github/workflows/rust-release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {},
|
||||
"x86_64_linux_musl": {},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"tag_prefix": "v",
|
||||
"rust_crate": "${package}",
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"signing": {
|
||||
"version_range": ">= 0.8.5",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "taiki-e/github-actions/.github/workflows/rust-release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"tag_prefix": "v",
|
||||
"rust_crate": "${package}",
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"signing": {
|
||||
"version_range": ">= 0.1.37",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "taiki-e/github-actions/.github/workflows/rust-release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"tag_prefix": "v",
|
||||
"rust_crate": "${package}",
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"signing": {
|
||||
"version_range": ">= 0.2.23",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "taiki-e/github-actions/.github/workflows/rust-release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"bin": ["${package}${exe}", "${package}-cp${exe}"],
|
||||
"version_range": ">= 1.0.0",
|
||||
"signing": {
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/ci.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
"${package}-${rust_target}.tar.gz",
|
||||
"${package}-${rust_target}.zip"
|
||||
],
|
||||
"signing": {
|
||||
"version_range": ">= 0.6.16",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "taiki-e/github-actions/.github/workflows/rust-release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {},
|
||||
"x86_64_linux_musl": {},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"tag_prefix": "v",
|
||||
"rust_crate": "${package}",
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"signing": {
|
||||
"version_range": ">= 0.1.5",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "taiki-e/github-actions/.github/workflows/rust-release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"bin": "${package}-${rust_target}/${package}${exe}",
|
||||
"version_range": ">= 0.2.20",
|
||||
"signing": {
|
||||
"version_range": ">= 0.3.1",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"tag_prefix": "v",
|
||||
"bin": "${package}${exe}",
|
||||
"version_range": ">= 0.62.0",
|
||||
"signing": {
|
||||
"version_range": ">= 0.69.4",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/reusable-release.yaml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {
|
||||
"asset_name": "${package}_${version}_Linux-64bit.tar.gz"
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"license_markdown": "[Apache-2.0](https://github.com/astral-sh/uv/blob/main/LICENSE-APACHE) OR [MIT](https://github.com/astral-sh/uv/blob/main/LICENSE-MIT)",
|
||||
"tag_prefix": "",
|
||||
"version_range": ">= 0.8.16",
|
||||
"signing": {
|
||||
"version_range": ">= 0.9.13",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {
|
||||
"asset_name": "${package}-x86_64-unknown-linux-musl.tar.gz",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
"tag_prefix": "wash-v",
|
||||
"rust_crate": "${package}",
|
||||
"asset_name": "${package}-${rust_target}${exe}",
|
||||
"signing": {
|
||||
"version_range": ">= 2.0.0",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/wash.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_musl": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
"rust_crate": "${package}-cli",
|
||||
"asset_name": "${package}-v${version}-${rust_target_arch}-${rust_target_os}.tar.xz",
|
||||
"bin": "${package}-v${version}-${rust_target_arch}-${rust_target_os}/${package}${exe}",
|
||||
"signing": {
|
||||
"version_range": ">= 28.0.0",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/publish-artifacts.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
"rust_crate": "${package}",
|
||||
"asset_name": "${package}-${rust_target}.tar.gz",
|
||||
"version_range": ">= 1.9.0",
|
||||
"signing": {
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/release-binaries.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {},
|
||||
"x86_64_macos": {},
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
"repository": "https://github.com/getzola/zola",
|
||||
"tag_prefix": "v",
|
||||
"asset_name": "${package}-v${version}-${rust_target}.tar.gz",
|
||||
"signing": {
|
||||
"version_range": ">= 0.20.0",
|
||||
"kind": {
|
||||
"gh-attestation": {
|
||||
"signer-workflow": "${repo}/.github/workflows/release.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"x86_64_linux_gnu": {},
|
||||
"x86_64_linux_musl": {},
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct BaseManifest {
|
||||
/// Markdown syntax for links to licenses. Automatically detected if possible.
|
||||
pub license_markdown: Option<String>,
|
||||
/// Prefix of release tag.
|
||||
pub tag_prefix: String,
|
||||
pub tag_prefix: StringOrArray,
|
||||
/// Crate name, if this is Rust crate.
|
||||
pub rust_crate: Option<String>,
|
||||
pub default_major_version: Option<String>,
|
||||
@@ -67,6 +67,7 @@ impl BaseManifest {
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Signing {
|
||||
pub version_range: Option<String>,
|
||||
pub kind: SigningKind,
|
||||
}
|
||||
|
||||
@@ -74,6 +75,10 @@ pub struct Signing {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum SigningKind {
|
||||
/// gh attestation
|
||||
/// <https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations>
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
GhAttestation { signer_workflow: String },
|
||||
/// algorithm: minisign
|
||||
/// public key: package.metadata.binstall.signing.pubkey at Cargo.toml
|
||||
/// <https://github.com/cargo-bins/cargo-binstall/blob/HEAD/SIGNING.md>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
#[macro_use]
|
||||
mod process;
|
||||
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
@@ -15,7 +18,7 @@ use anyhow::{Context as _, Result, bail};
|
||||
use fs_err as fs;
|
||||
use install_action_internal_codegen::{
|
||||
BaseManifest, HostPlatform, Manifest, ManifestDownloadInfo, ManifestRef, ManifestTemplate,
|
||||
ManifestTemplateDownloadInfo, Manifests, Signing, SigningKind, Version, workspace_root,
|
||||
ManifestTemplateDownloadInfo, Manifests, SigningKind, Version, workspace_root,
|
||||
};
|
||||
use spdx::expression::{ExprNode, ExpressionReq, Operator};
|
||||
|
||||
@@ -76,7 +79,14 @@ fn main() -> Result<()> {
|
||||
if release.prerelease {
|
||||
return None;
|
||||
}
|
||||
let version = release.tag_name.strip_prefix(&base_info.tag_prefix)?;
|
||||
let mut version = None;
|
||||
for tag_prefix in base_info.tag_prefix.as_slice() {
|
||||
if let Some(v) = release.tag_name.strip_prefix(tag_prefix) {
|
||||
version = Some(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let version = version?;
|
||||
let mut semver_version = version.parse::<semver::Version>();
|
||||
if semver_version.is_err() {
|
||||
if let Some(default_major_version) = &base_info.default_major_version {
|
||||
@@ -215,15 +225,15 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let version_req: Option<semver::VersionReq> = match version_req {
|
||||
let version_req: semver::VersionReq = match version_req {
|
||||
_ if latest_only => {
|
||||
let req = format!("={}", releases.first_key_value().unwrap().0.0).parse()?;
|
||||
eprintln!("update manifest for versions '{req}'");
|
||||
Some(req)
|
||||
req
|
||||
}
|
||||
None => match base_info.version_range {
|
||||
Some(version_range) => Some(version_range.parse()?),
|
||||
None => Some(">= 0.0.1".parse()?), // HACK: ignore pre-releases
|
||||
Some(version_range) => version_range.parse()?,
|
||||
None => ">= 0.0.1".parse()?, // HACK: ignore pre-releases
|
||||
},
|
||||
Some(version_req) => {
|
||||
for version in manifests.map.keys() {
|
||||
@@ -247,17 +257,15 @@ fn main() -> Result<()> {
|
||||
version_req.parse()?
|
||||
};
|
||||
eprintln!("update manifest for versions '{req}'");
|
||||
Some(req)
|
||||
req
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf = vec![];
|
||||
let mut buf2 = vec![];
|
||||
for (Reverse(semver_version), (version, release)) in &releases {
|
||||
if let Some(version_req) = &version_req {
|
||||
if !version_req.matches(semver_version) {
|
||||
continue;
|
||||
}
|
||||
if !version_req.matches(semver_version) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Specifically skip versions of xbuild with build metadata.
|
||||
@@ -274,6 +282,28 @@ fn main() -> Result<()> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let signing_version_req: Option<semver::VersionReq> = match &base_info.signing {
|
||||
Some(signing) => {
|
||||
match &signing.version_range {
|
||||
Some(version_range) => Some(version_range.parse()?),
|
||||
None => Some(">= 0.0.1".parse()?), // HACK: ignore pre-releases
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if let Some(asset) = release.assets.iter().find(|asset| {
|
||||
asset.name.contains(".asc")
|
||||
|| asset.name.contains(".gpg")
|
||||
|| asset.name.contains(".sig")
|
||||
}) {
|
||||
eprintln!(
|
||||
"{package} may supports other signing verification method using {}",
|
||||
asset.name
|
||||
);
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut download_info = BTreeMap::new();
|
||||
let mut pubkey = None;
|
||||
for (&platform, base_download_info) in &base_info.platform {
|
||||
@@ -323,6 +353,7 @@ fn main() -> Result<()> {
|
||||
if let Some(entry) = manifest.download_info.get(&platform) {
|
||||
if entry.etag == etag {
|
||||
eprintln!("existing etag matched");
|
||||
// NB: Comment out these two lines when adding verification for old release.
|
||||
download_info.insert(platform, entry.clone());
|
||||
continue;
|
||||
}
|
||||
@@ -354,81 +385,103 @@ fn main() -> Result<()> {
|
||||
eprintln!("{hash} *{asset_name}");
|
||||
let bin_url = &url;
|
||||
|
||||
match base_info.signing {
|
||||
Some(Signing { kind: SigningKind::MinisignBinstall }) => {
|
||||
let url = url.clone() + ".sig";
|
||||
let sig_download_cache = &download_cache.with_extension(format!(
|
||||
"{}.sig",
|
||||
download_cache.extension().unwrap_or_default().to_str().unwrap()
|
||||
));
|
||||
eprint!("downloading {url} for signature validation ... ");
|
||||
let sig = if sig_download_cache.is_file() {
|
||||
eprintln!("already downloaded");
|
||||
minisign_verify::Signature::from_file(sig_download_cache)?
|
||||
} else {
|
||||
let buf = download(&url)?.into_string()?;
|
||||
eprintln!("download complete");
|
||||
fs::write(sig_download_cache, &buf)?;
|
||||
minisign_verify::Signature::decode(&buf)?
|
||||
};
|
||||
if let Some(signing) = &base_info.signing {
|
||||
match &signing.kind {
|
||||
_ if !signing_version_req.as_ref().unwrap().matches(semver_version) => {}
|
||||
SigningKind::GhAttestation { signer_workflow } => {
|
||||
eprintln!("verifying {url} with gh attestation verify");
|
||||
let signer_workflow = signer_workflow.replace("${repo}", repo);
|
||||
cmd!(
|
||||
"gh",
|
||||
"attestation",
|
||||
"verify",
|
||||
"--repo",
|
||||
repo,
|
||||
"--signer-workflow",
|
||||
signer_workflow,
|
||||
&download_cache
|
||||
)
|
||||
.run()?;
|
||||
}
|
||||
SigningKind::MinisignBinstall => {
|
||||
let url = url.clone() + ".sig";
|
||||
let sig_download_cache = &download_cache.with_extension(format!(
|
||||
"{}.sig",
|
||||
download_cache.extension().unwrap_or_default().to_str().unwrap()
|
||||
));
|
||||
eprint!("downloading {url} for signature validation ... ");
|
||||
let sig = if sig_download_cache.is_file() {
|
||||
eprintln!("already downloaded");
|
||||
minisign_verify::Signature::from_file(sig_download_cache)?
|
||||
} else {
|
||||
let buf = download(&url)?.into_string()?;
|
||||
eprintln!("download complete");
|
||||
fs::write(sig_download_cache, &buf)?;
|
||||
minisign_verify::Signature::decode(&buf)?
|
||||
};
|
||||
|
||||
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.versions.iter().find(|v| v.num == *semver_version).unwrap();
|
||||
let url = format!("https://crates.io{}", v.dl_path);
|
||||
let crate_download_cache =
|
||||
&download_cache_dir.join(format!("{version}-Cargo.toml"));
|
||||
eprint!("downloading {url} for signature verification ... ");
|
||||
if crate_download_cache.is_file() {
|
||||
eprintln!("already downloaded");
|
||||
} else {
|
||||
download(&url)?.into_reader().read_to_end(&mut buf2)?;
|
||||
let hash = ring::digest::digest(&ring::digest::SHA256, &buf2);
|
||||
if format!("{hash:?}").strip_prefix("SHA256:").unwrap() != v.checksum {
|
||||
bail!("checksum mismatch for {url}");
|
||||
}
|
||||
let decoder = flate2::read::GzDecoder::new(&*buf2);
|
||||
let mut archive = tar::Archive::new(decoder);
|
||||
for entry in archive.entries()? {
|
||||
let mut entry = entry?;
|
||||
let path = entry.path()?;
|
||||
if path.file_name() == Some(OsStr::new("Cargo.toml")) {
|
||||
entry.unpack(crate_download_cache)?;
|
||||
break;
|
||||
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
|
||||
.versions
|
||||
.iter()
|
||||
.find(|v| v.num == *semver_version)
|
||||
.unwrap();
|
||||
let url = format!("https://crates.io{}", v.dl_path);
|
||||
let crate_download_cache =
|
||||
&download_cache_dir.join(format!("{version}-Cargo.toml"));
|
||||
eprint!("downloading {url} for signature verification ... ");
|
||||
if crate_download_cache.is_file() {
|
||||
eprintln!("already downloaded");
|
||||
} else {
|
||||
download(&url)?.into_reader().read_to_end(&mut buf2)?;
|
||||
let hash = ring::digest::digest(&ring::digest::SHA256, &buf2);
|
||||
if format!("{hash:?}").strip_prefix("SHA256:").unwrap() != v.checksum {
|
||||
bail!("checksum mismatch for {url}");
|
||||
}
|
||||
let decoder = flate2::read::GzDecoder::new(&*buf2);
|
||||
let mut archive = tar::Archive::new(decoder);
|
||||
for entry in archive.entries()? {
|
||||
let mut entry = entry?;
|
||||
let path = entry.path()?;
|
||||
if path.file_name() == Some(OsStr::new("Cargo.toml")) {
|
||||
entry.unpack(crate_download_cache)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf2.clear();
|
||||
eprintln!("download complete");
|
||||
}
|
||||
buf2.clear();
|
||||
eprintln!("download complete");
|
||||
if pubkey.is_none() {
|
||||
let cargo_manifest = toml::de::from_str::<cargo_manifest::Manifest>(
|
||||
&fs::read_to_string(crate_download_cache)?,
|
||||
)?;
|
||||
eprintln!(
|
||||
"algorithm: {}",
|
||||
cargo_manifest.package.metadata.binstall.signing.algorithm
|
||||
);
|
||||
eprintln!(
|
||||
"pubkey: {}",
|
||||
cargo_manifest.package.metadata.binstall.signing.pubkey
|
||||
);
|
||||
assert_eq!(
|
||||
cargo_manifest.package.metadata.binstall.signing.algorithm,
|
||||
"minisign"
|
||||
);
|
||||
pubkey = Some(minisign_verify::PublicKey::from_base64(
|
||||
&cargo_manifest.package.metadata.binstall.signing.pubkey,
|
||||
)?);
|
||||
}
|
||||
let pubkey = pubkey.as_ref().unwrap();
|
||||
eprint!("verifying signature for {bin_url} ... ");
|
||||
let allow_legacy = false;
|
||||
pubkey.verify(&buf, &sig, allow_legacy)?;
|
||||
eprintln!("done");
|
||||
}
|
||||
if pubkey.is_none() {
|
||||
let cargo_manifest = toml::de::from_str::<cargo_manifest::Manifest>(
|
||||
&fs::read_to_string(crate_download_cache)?,
|
||||
)?;
|
||||
eprintln!(
|
||||
"algorithm: {}",
|
||||
cargo_manifest.package.metadata.binstall.signing.algorithm
|
||||
);
|
||||
eprintln!(
|
||||
"pubkey: {}",
|
||||
cargo_manifest.package.metadata.binstall.signing.pubkey
|
||||
);
|
||||
assert_eq!(
|
||||
cargo_manifest.package.metadata.binstall.signing.algorithm,
|
||||
"minisign"
|
||||
);
|
||||
pubkey = Some(minisign_verify::PublicKey::from_base64(
|
||||
&cargo_manifest.package.metadata.binstall.signing.pubkey,
|
||||
)?);
|
||||
}
|
||||
let pubkey = pubkey.as_ref().unwrap();
|
||||
eprint!("verifying signature for {bin_url} ... ");
|
||||
let allow_legacy = false;
|
||||
pubkey.verify(&buf, &sig, allow_legacy)?;
|
||||
eprintln!("done");
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
download_info.insert(
|
||||
|
||||
155
tools/codegen/src/process.rs
Normal file
155
tools/codegen/src/process.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
use std::{
|
||||
fmt, format,
|
||||
process::{Command, ExitStatus, Output},
|
||||
str,
|
||||
string::{String, ToString as _},
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Error, Result};
|
||||
|
||||
macro_rules! cmd {
|
||||
($program:expr $(, $arg:expr)* $(,)?) => {{
|
||||
let mut _cmd = std::process::Command::new($program);
|
||||
$(
|
||||
_cmd.arg($arg);
|
||||
)*
|
||||
$crate::process::ProcessBuilder::from_std(_cmd)
|
||||
}};
|
||||
}
|
||||
|
||||
// A builder for an external process, inspired by https://github.com/rust-lang/cargo/blob/0.47.0/src/cargo/util/process_builder.rs
|
||||
#[must_use]
|
||||
pub(crate) struct ProcessBuilder {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
impl ProcessBuilder {
|
||||
pub(crate) fn from_std(cmd: Command) -> Self {
|
||||
Self { cmd }
|
||||
}
|
||||
|
||||
// pub(crate) fn into_std(self) -> Command {
|
||||
// self.cmd
|
||||
// }
|
||||
|
||||
// /// Adds an argument to pass to the program.
|
||||
// pub(crate) fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
|
||||
// self.cmd.arg(arg.as_ref());
|
||||
// self
|
||||
// }
|
||||
|
||||
// /// Adds multiple arguments to pass to the program.
|
||||
// pub(crate) fn args(&mut self, args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> &mut Self {
|
||||
// self.cmd.args(args);
|
||||
// self
|
||||
// }
|
||||
|
||||
// /// Set a variable in the process's environment.
|
||||
// pub(crate) fn env(&mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> &mut Self {
|
||||
// self.cmd.env(key.as_ref(), val.as_ref());
|
||||
// self
|
||||
// }
|
||||
|
||||
/// Executes a process, waiting for completion, and mapping non-zero exit
|
||||
/// status to an error.
|
||||
pub(crate) fn run(&mut self) -> Result<()> {
|
||||
let status = self.cmd.status().with_context(|| {
|
||||
process_error(format!("could not execute process {self}"), None, None)
|
||||
})?;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(process_error(
|
||||
format!("process didn't exit successfully: {self}"),
|
||||
Some(status),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// /// Executes a process, captures its stdio output, returning the captured
|
||||
// /// output, or an error if non-zero exit status.
|
||||
// pub(crate) fn run_with_output(&mut self) -> Result<Output> {
|
||||
// let output = self.cmd.output().with_context(|| {
|
||||
// process_error(format!("could not execute process {self}"), None, None)
|
||||
// })?;
|
||||
// if output.status.success() {
|
||||
// Ok(output)
|
||||
// } else {
|
||||
// Err(process_error(
|
||||
// format!("process didn't exit successfully: {self}"),
|
||||
// Some(output.status),
|
||||
// Some(&output),
|
||||
// ))
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Executes a process, captures its stdio output, returning the captured
|
||||
// /// standard output as a `String`.
|
||||
// pub(crate) fn read(&mut self) -> Result<String> {
|
||||
// let mut output = String::from_utf8(self.run_with_output()?.stdout)
|
||||
// .with_context(|| format!("failed to parse output from {self}"))?;
|
||||
// while output.ends_with('\n') || output.ends_with('\r') {
|
||||
// output.pop();
|
||||
// }
|
||||
// Ok(output)
|
||||
// }
|
||||
}
|
||||
|
||||
// Based on https://github.com/rust-lang/cargo/blob/0.47.0/src/cargo/util/process_builder.rs
|
||||
impl fmt::Display for ProcessBuilder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !f.alternate() {
|
||||
f.write_str("`")?;
|
||||
}
|
||||
|
||||
f.write_str(&self.cmd.get_program().to_string_lossy())?;
|
||||
|
||||
for arg in self.cmd.get_args() {
|
||||
write!(f, " {}", arg.to_string_lossy())?;
|
||||
}
|
||||
|
||||
if !f.alternate() {
|
||||
f.write_str("`")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Based on https://github.com/rust-lang/cargo/blob/0.47.0/src/cargo/util/errors.rs
|
||||
/// Creates a new process error.
|
||||
///
|
||||
/// `status` can be `None` if the process did not launch.
|
||||
/// `output` can be `None` if the process did not launch, or output was not captured.
|
||||
fn process_error(mut msg: String, status: Option<ExitStatus>, output: Option<&Output>) -> Error {
|
||||
match status {
|
||||
Some(s) => {
|
||||
msg.push_str(" (");
|
||||
msg.push_str(&s.to_string());
|
||||
msg.push(')');
|
||||
}
|
||||
None => msg.push_str(" (never executed)"),
|
||||
}
|
||||
|
||||
if let Some(out) = output {
|
||||
match str::from_utf8(&out.stdout) {
|
||||
Ok(s) if !s.trim_start().is_empty() => {
|
||||
msg.push_str("\n--- stdout\n");
|
||||
msg.push_str(s);
|
||||
}
|
||||
Ok(_) | Err(_) => {}
|
||||
}
|
||||
match str::from_utf8(&out.stderr) {
|
||||
Ok(s) if !s.trim_start().is_empty() => {
|
||||
msg.push_str("\n--- stderr\n");
|
||||
msg.push_str(s);
|
||||
}
|
||||
Ok(_) | Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Error::msg(msg)
|
||||
}
|
||||
Reference in New Issue
Block a user