feat: defer artifact loading

This commit is contained in:
2025-06-05 13:09:30 +01:00
parent 0502845986
commit 05d17f3682
5 changed files with 89 additions and 61 deletions

36
Cargo.lock generated
View File

@ -1299,19 +1299,6 @@ dependencies = [
"hashbrown 0.15.2",
]
[[package]]
name = "indicatif"
version = "0.17.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
dependencies = [
"console",
"number_prefix",
"portable-atomic",
"unicode-width",
"web-time",
]
[[package]]
name = "inout"
version = "0.1.3"
@ -1486,7 +1473,6 @@ dependencies = [
"config",
"dialoguer",
"env_logger",
"indicatif",
"log",
"nostr-sdk",
"reqwest",
@ -1656,12 +1642,6 @@ dependencies = [
"libm",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "object"
version = "0.36.7"
@ -1897,12 +1877,6 @@ dependencies = [
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@ -3081,16 +3055,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.26.8"

View File

@ -21,7 +21,6 @@ tokio = { version = "1.43.0", features = ["fs", "rt", "macros", "rt-multi-thread
serde = { version = "1.0.217", features = ["derive"] }
async-trait = "0.1.86"
semver = "1.0.25"
indicatif = "0.17.11"
dialoguer = "0.11.0"
env_logger = "0.11.6"
sha2 = "0.10.8"

View File

@ -9,10 +9,11 @@ use config::{Config, File};
use log::info;
use nostr_sdk::prelude::Coordinate;
use nostr_sdk::{Client, EventBuilder, Keys, Kind, Tag};
use semver::Version;
use std::path::PathBuf;
#[derive(clap::Parser)]
#[command(version, about)]
#[command(about)]
struct Args {
/// User specified config path
#[arg(long, short)]
@ -21,6 +22,10 @@ struct Args {
/// Relay to publish events to
#[arg(long)]
pub relay: Vec<String>,
/// Use specific version
#[arg(long)]
pub version: Option<Version>,
}
#[tokio::main]
@ -41,16 +46,19 @@ async fn main() -> Result<()> {
let repo: Box<dyn Repo> = (&manifest).try_into()?;
let releases = repo.get_releases().await?;
let mut releases = repo.get_releases().await?;
info!("Found {} release(s)", releases.len());
if let Some(release) = releases.first() {
releases.sort_by(|a, b| b.version.partial_cmp(&a.version).unwrap());
if let Some(release) = releases.iter_mut().find(|r| {
args.version
.as_ref()
.map(|v| v == &r.version)
.unwrap_or(true)
}) {
info!("Starting publish of release {}", release.version);
info!("Artifacts: ");
for a in &release.artifacts {
info!(" - {}", a);
}
if !dialoguer::Confirm::new()
.default(false)
.with_prompt(format!("Publish v{}?", release.version))
@ -59,6 +67,20 @@ async fn main() -> Result<()> {
return Ok(());
}
release.load_artifacts().await?;
info!("Artifacts: ");
for a in &release.artifacts {
info!(" - {}", a);
}
if !dialoguer::Confirm::new()
.default(false)
.with_prompt("Continue?")
.interact()?
{
return Ok(());
}
let key = dialoguer::Password::new()
.with_prompt("Enter nsec:")
.interact()?;

View File

@ -1,6 +1,7 @@
use crate::repo::{load_artifact_url, Repo, RepoRelease};
use crate::repo::{ArtifactMetadata, Platform, Repo, RepoArtifact, RepoRelease, RepoResource};
use anyhow::{anyhow, Result};
use log::{info, warn};
use nostr_sdk::prelude::hex;
use nostr_sdk::Url;
use reqwest::header::{HeaderMap, ACCEPT, USER_AGENT};
use reqwest::Client;
@ -60,6 +61,19 @@ struct GithubReleaseArtifact {
pub size: u64,
pub content_type: String,
pub browser_download_url: String,
pub digest: Option<String>,
}
fn decode_digest(digest: &Option<String>) -> Result<Vec<u8>> {
if let Some(s) = digest {
if s.starts_with("sha256:") {
Ok(hex::decode(&s[7..])?)
} else {
Ok(vec![])
}
} else {
Ok(vec![])
}
}
#[async_trait::async_trait]
@ -83,31 +97,40 @@ impl Repo for GithubRepo {
for release in gh_release {
let mut artifacts = vec![];
for gh_artifact in release.assets {
match load_artifact_url(&gh_artifact.browser_download_url).await {
Ok(a) => artifacts.push(a),
Err(e) => warn!(
"Failed to load artifact {}: {}",
gh_artifact.browser_download_url, e
),
}
artifacts.push(RepoArtifact {
name: gh_artifact.name,
size: gh_artifact.size,
location: RepoResource::Remote(gh_artifact.browser_download_url),
content_type: gh_artifact.content_type,
platform: Platform::Unknown,
metadata: ArtifactMetadata::Empty,
hash: decode_digest(&gh_artifact.digest)?,
})
}
if artifacts.is_empty() {
warn!("No artifacts found for {}", release.tag_name);
continue;
}
let version = match Version::parse(if release.tag_name.starts_with("v") {
&release.tag_name[1..]
} else {
&release.tag_name
}) {
Ok(v) => v,
Err(e) => {
warn!(
"Could not load version for release {} {}",
release.tag_name, e
);
continue;
}
};
releases.push(RepoRelease {
version: Version::parse(if release.tag_name.starts_with("v") {
&release.tag_name[1..]
} else {
&release.tag_name
})?,
version,
description: Some(release.body),
url: Some(release.url),
artifacts,
});
//TODO: handle more than one release
break;
}
Ok(releases)
}

View File

@ -115,6 +115,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
])?);
}
}
ArtifactMetadata::Empty => {}
}
Ok(b)
}
@ -122,6 +123,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
#[derive(Debug, Clone)]
pub enum ArtifactMetadata {
Empty,
APK {
manifest: AndroidManifest,
signature_blocks: Vec<ApkSignatureBlock>,
@ -148,6 +150,7 @@ impl Display for ArtifactMetadata {
.join(", ")
)
}
ArtifactMetadata::Empty => write!(f, "empty"),
}
}
}
@ -160,6 +163,7 @@ pub enum Platform {
Windows { arch: Architecture },
Linux { arch: Architecture },
Web,
Unknown,
}
impl Display for Platform {
@ -215,6 +219,7 @@ impl Display for Platform {
}
),
Platform::Web => write!(f, "web"),
Platform::Unknown => write!(f, "unknown"),
}
}
}
@ -314,6 +319,21 @@ impl RepoRelease {
ret.push(b.sign(signer).await?);
Ok(ret)
}
/// Download and prepare release artifacts
pub async fn load_artifacts(&mut self) -> Result<()> {
let mut new_artifacts = vec![];
for a in &self.artifacts {
if let RepoResource::Remote(url) = &a.location {
match load_artifact_url(&url).await {
Ok(artifact) => new_artifacts.push(artifact),
Err(e) => warn!("Failed to load artifact {}: {}", &url, e),
}
}
}
self.artifacts = new_artifacts;
Ok(())
}
}
/// Generic artifact repository
@ -344,7 +364,6 @@ impl TryInto<Box<dyn Repo>> for &Manifest {
async fn load_artifact_url(url: &str) -> Result<RepoArtifact> {
info!("Downloading artifact {}", url);
let u = Url::parse(url)?;
let rsp = reqwest::get(u.clone()).await?;
let id = hex::encode(Sha256::digest(url.as_bytes()));
let mut tmp = temp_dir().join(id);
tmp.set_extension(
@ -355,6 +374,7 @@ async fn load_artifact_url(url: &str) -> Result<RepoArtifact> {
.unwrap(),
);
if !tmp.exists() {
let rsp = reqwest::get(u.clone()).await?;
let mut tmp_file = tokio::fs::File::create(&tmp).await?;
let mut rsp_stream = rsp.bytes_stream();
while let Some(data) = rsp_stream.next().await {