feat: defer artifact loading
This commit is contained in:
36
Cargo.lock
generated
36
Cargo.lock
generated
@ -1299,19 +1299,6 @@ dependencies = [
|
|||||||
"hashbrown 0.15.2",
|
"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]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -1486,7 +1473,6 @@ dependencies = [
|
|||||||
"config",
|
"config",
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"indicatif",
|
|
||||||
"log",
|
"log",
|
||||||
"nostr-sdk",
|
"nostr-sdk",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -1656,12 +1642,6 @@ dependencies = [
|
|||||||
"libm",
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "number_prefix"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@ -1897,12 +1877,6 @@ dependencies = [
|
|||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "portable-atomic"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
@ -3081,16 +3055,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"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]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.26.8"
|
version = "0.26.8"
|
||||||
|
@ -21,7 +21,6 @@ tokio = { version = "1.43.0", features = ["fs", "rt", "macros", "rt-multi-thread
|
|||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
async-trait = "0.1.86"
|
async-trait = "0.1.86"
|
||||||
semver = "1.0.25"
|
semver = "1.0.25"
|
||||||
indicatif = "0.17.11"
|
|
||||||
dialoguer = "0.11.0"
|
dialoguer = "0.11.0"
|
||||||
env_logger = "0.11.6"
|
env_logger = "0.11.6"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
|
36
src/main.rs
36
src/main.rs
@ -9,10 +9,11 @@ use config::{Config, File};
|
|||||||
use log::info;
|
use log::info;
|
||||||
use nostr_sdk::prelude::Coordinate;
|
use nostr_sdk::prelude::Coordinate;
|
||||||
use nostr_sdk::{Client, EventBuilder, Keys, Kind, Tag};
|
use nostr_sdk::{Client, EventBuilder, Keys, Kind, Tag};
|
||||||
|
use semver::Version;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(clap::Parser)]
|
#[derive(clap::Parser)]
|
||||||
#[command(version, about)]
|
#[command(about)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// User specified config path
|
/// User specified config path
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
@ -21,6 +22,10 @@ struct Args {
|
|||||||
/// Relay to publish events to
|
/// Relay to publish events to
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub relay: Vec<String>,
|
pub relay: Vec<String>,
|
||||||
|
|
||||||
|
/// Use specific version
|
||||||
|
#[arg(long)]
|
||||||
|
pub version: Option<Version>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -41,16 +46,19 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let repo: Box<dyn Repo> = (&manifest).try_into()?;
|
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());
|
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!("Starting publish of release {}", release.version);
|
||||||
info!("Artifacts: ");
|
|
||||||
for a in &release.artifacts {
|
|
||||||
info!(" - {}", a);
|
|
||||||
}
|
|
||||||
if !dialoguer::Confirm::new()
|
if !dialoguer::Confirm::new()
|
||||||
.default(false)
|
.default(false)
|
||||||
.with_prompt(format!("Publish v{}?", release.version))
|
.with_prompt(format!("Publish v{}?", release.version))
|
||||||
@ -59,6 +67,20 @@ async fn main() -> Result<()> {
|
|||||||
return Ok(());
|
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()
|
let key = dialoguer::Password::new()
|
||||||
.with_prompt("Enter nsec:")
|
.with_prompt("Enter nsec:")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
@ -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 anyhow::{anyhow, Result};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use nostr_sdk::prelude::hex;
|
||||||
use nostr_sdk::Url;
|
use nostr_sdk::Url;
|
||||||
use reqwest::header::{HeaderMap, ACCEPT, USER_AGENT};
|
use reqwest::header::{HeaderMap, ACCEPT, USER_AGENT};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
@ -60,6 +61,19 @@ struct GithubReleaseArtifact {
|
|||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub content_type: String,
|
pub content_type: String,
|
||||||
pub browser_download_url: 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]
|
#[async_trait::async_trait]
|
||||||
@ -83,31 +97,40 @@ impl Repo for GithubRepo {
|
|||||||
for release in gh_release {
|
for release in gh_release {
|
||||||
let mut artifacts = vec![];
|
let mut artifacts = vec![];
|
||||||
for gh_artifact in release.assets {
|
for gh_artifact in release.assets {
|
||||||
match load_artifact_url(&gh_artifact.browser_download_url).await {
|
artifacts.push(RepoArtifact {
|
||||||
Ok(a) => artifacts.push(a),
|
name: gh_artifact.name,
|
||||||
Err(e) => warn!(
|
size: gh_artifact.size,
|
||||||
"Failed to load artifact {}: {}",
|
location: RepoResource::Remote(gh_artifact.browser_download_url),
|
||||||
gh_artifact.browser_download_url, e
|
content_type: gh_artifact.content_type,
|
||||||
),
|
platform: Platform::Unknown,
|
||||||
}
|
metadata: ArtifactMetadata::Empty,
|
||||||
|
hash: decode_digest(&gh_artifact.digest)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if artifacts.is_empty() {
|
if artifacts.is_empty() {
|
||||||
warn!("No artifacts found for {}", release.tag_name);
|
warn!("No artifacts found for {}", release.tag_name);
|
||||||
continue;
|
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 {
|
releases.push(RepoRelease {
|
||||||
version: Version::parse(if release.tag_name.starts_with("v") {
|
version,
|
||||||
&release.tag_name[1..]
|
|
||||||
} else {
|
|
||||||
&release.tag_name
|
|
||||||
})?,
|
|
||||||
description: Some(release.body),
|
description: Some(release.body),
|
||||||
url: Some(release.url),
|
url: Some(release.url),
|
||||||
artifacts,
|
artifacts,
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: handle more than one release
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
Ok(releases)
|
Ok(releases)
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
|||||||
])?);
|
])?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ArtifactMetadata::Empty => {}
|
||||||
}
|
}
|
||||||
Ok(b)
|
Ok(b)
|
||||||
}
|
}
|
||||||
@ -122,6 +123,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ArtifactMetadata {
|
pub enum ArtifactMetadata {
|
||||||
|
Empty,
|
||||||
APK {
|
APK {
|
||||||
manifest: AndroidManifest,
|
manifest: AndroidManifest,
|
||||||
signature_blocks: Vec<ApkSignatureBlock>,
|
signature_blocks: Vec<ApkSignatureBlock>,
|
||||||
@ -148,6 +150,7 @@ impl Display for ArtifactMetadata {
|
|||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ArtifactMetadata::Empty => write!(f, "empty"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,6 +163,7 @@ pub enum Platform {
|
|||||||
Windows { arch: Architecture },
|
Windows { arch: Architecture },
|
||||||
Linux { arch: Architecture },
|
Linux { arch: Architecture },
|
||||||
Web,
|
Web,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Platform {
|
impl Display for Platform {
|
||||||
@ -215,6 +219,7 @@ impl Display for Platform {
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
Platform::Web => write!(f, "web"),
|
Platform::Web => write!(f, "web"),
|
||||||
|
Platform::Unknown => write!(f, "unknown"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,6 +319,21 @@ impl RepoRelease {
|
|||||||
ret.push(b.sign(signer).await?);
|
ret.push(b.sign(signer).await?);
|
||||||
Ok(ret)
|
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
|
/// Generic artifact repository
|
||||||
@ -344,7 +364,6 @@ impl TryInto<Box<dyn Repo>> for &Manifest {
|
|||||||
async fn load_artifact_url(url: &str) -> Result<RepoArtifact> {
|
async fn load_artifact_url(url: &str) -> Result<RepoArtifact> {
|
||||||
info!("Downloading artifact {}", url);
|
info!("Downloading artifact {}", url);
|
||||||
let u = Url::parse(url)?;
|
let u = Url::parse(url)?;
|
||||||
let rsp = reqwest::get(u.clone()).await?;
|
|
||||||
let id = hex::encode(Sha256::digest(url.as_bytes()));
|
let id = hex::encode(Sha256::digest(url.as_bytes()));
|
||||||
let mut tmp = temp_dir().join(id);
|
let mut tmp = temp_dir().join(id);
|
||||||
tmp.set_extension(
|
tmp.set_extension(
|
||||||
@ -355,6 +374,7 @@ async fn load_artifact_url(url: &str) -> Result<RepoArtifact> {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
if !tmp.exists() {
|
if !tmp.exists() {
|
||||||
|
let rsp = reqwest::get(u.clone()).await?;
|
||||||
let mut tmp_file = tokio::fs::File::create(&tmp).await?;
|
let mut tmp_file = tokio::fs::File::create(&tmp).await?;
|
||||||
let mut rsp_stream = rsp.bytes_stream();
|
let mut rsp_stream = rsp.bytes_stream();
|
||||||
while let Some(data) = rsp_stream.next().await {
|
while let Some(data) = rsp_stream.next().await {
|
||||||
|
Reference in New Issue
Block a user