Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
05d17f3682
|
36
Cargo.lock
generated
36
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
36
src/main.rs
36
src/main.rs
@ -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()?;
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user