From 407255efac74678d05b0c3a8cf9e26ef530c1b63 Mon Sep 17 00:00:00 2001 From: Anthony Griffon Date: Thu, 30 Nov 2023 10:32:25 +0100 Subject: [PATCH] feat: add update mechanism Signed-off-by: Anthony Griffon --- .gitignore | 1 + Cargo.lock | 191 ++++++++++++++++++++++++++ ci/build-m1.sh | 38 +++++ cli/Cargo.toml | 2 +- cli/src/application/update/mod.rs | 37 ++++- cli/src/infrastructure/mod.rs | 2 + cli/src/infrastructure/updater/mod.rs | 33 +++++ cli/src/main.rs | 41 +++++- 8 files changed, 336 insertions(+), 9 deletions(-) create mode 100755 ci/build-m1.sh create mode 100644 cli/src/infrastructure/updater/mod.rs diff --git a/.gitignore b/.gitignore index 7216b6f..a23e2e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target test_project/ +deployment/ diff --git a/Cargo.lock b/Cargo.lock index 6b8e18b..d7d1cc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -234,6 +234,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "1.3.2" @@ -421,6 +427,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + [[package]] name = "core-foundation" version = "0.9.3" @@ -513,6 +525,34 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version 0.4.0", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "darling" version = "0.14.4" @@ -593,6 +633,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.9" @@ -700,6 +750,37 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -746,6 +827,24 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", +] + [[package]] name = "findshlibs" version = "0.10.2" @@ -1811,12 +1910,28 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" + [[package]] name = "portable-atomic" version = "1.5.1" @@ -1904,6 +2019,15 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -2215,6 +2339,8 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a34ad8e4a86884ab42e9b8690e9343abdcfe5fa38a0318cfe1565ba9ad437b4" dependencies = [ + "either", + "flate2", "hyper", "indicatif", "log", @@ -2224,8 +2350,10 @@ dependencies = [ "self-replace", "semver 1.0.20", "serde_json", + "tar", "tempfile", "urlencoding", + "zipsign-api", ] [[package]] @@ -2516,6 +2644,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "similar" version = "2.3.0" @@ -2582,12 +2720,28 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "swarmd" version = "0.1.10" @@ -2717,6 +2871,17 @@ dependencies = [ "libc", ] +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.8.1" @@ -3769,6 +3934,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "xattr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +dependencies = [ + "libc", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -3777,3 +3951,20 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zipsign-api" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba5aa1827d6b1a35a29b3413ec69ce5f796e4d897e3e5b38f461bef41d225ea" +dependencies = [ + "base64 0.21.5", + "ed25519-dalek", + "thiserror", +] diff --git a/ci/build-m1.sh b/ci/build-m1.sh new file mode 100755 index 0000000..2e61b9a --- /dev/null +++ b/ci/build-m1.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# From ripgrep: https://github.com/BurntSushi/ripgrep/blob/84d65865e6febc784b8b0296dd4681d761fa5a67/ci/build-and-publish-m2#L3 +# This script builds a swarmd release for the aarch64-apple-darwin target. +# +# Once GitHub Actions has proper support for Apple silicon, we should add it +# to our release workflow and drop this script. + +set -e + +version="$1" +if [ -z "$version" ]; then + echo "missing version" >&2 + echo "Usage: "$(basename "$0")" " >&2 + exit 1 +fi + +target=aarch64-apple-darwin +OPENSSL_STATIC=1 cargo build --bin swarmd --release --target $target +BIN=target/$target/release/swarmd +NAME=swarmd-$version-$target +ARCHIVE="deployment/m1/$NAME" + +mkdir -p "$ARCHIVE"/doc +cp "$BIN" "$ARCHIVE"/ +strip "$ARCHIVE/swarmd" +cp cli/README.md "$ARCHIVE"/ +cp cli/CHANGELOG.md "$ARCHIVE"/doc/ +# "$BIN" --generate complete-bash > "$ARCHIVE/complete/rg.bash" +# "$BIN" --generate complete-fish > "$ARCHIVE/complete/rg.fish" +# "$BIN" --generate complete-powershell > "$ARCHIVE/complete/_rg.ps1" +# "$BIN" --generate complete-zsh > "$ARCHIVE/complete/_rg" +# "$BIN" --generate man > "$ARCHIVE/doc/rg.1" + +# tar czf "$ARCHIVE.tar.gz" "$NAME" +tar czvf "$ARCHIVE.tar.gz" -C deployment/m1 "$NAME" +shasum -a 256 "$ARCHIVE.tar.gz" > "$ARCHIVE.tar.gz.sha256" +gh release upload "$version" "$ARCHIVE.tar.gz" "$ARCHIVE.tar.gz.sha256" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7d9d5a9..8a4f5a4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -45,7 +45,7 @@ serde_with = "3" swarmd_slug-rs.workspace = true swarmd_generated.workspace = true -self_update = "0.39" +self_update = { version = "0.39", features = ["archive-tar", "compression-flate2"] } # Log stack swarmd_instruments.workspace = true diff --git a/cli/src/application/update/mod.rs b/cli/src/application/update/mod.rs index c9e800f..76f953a 100644 --- a/cli/src/application/update/mod.rs +++ b/cli/src/application/update/mod.rs @@ -1,6 +1,9 @@ +use std::env::current_dir; + use crate::domain::Env; use clap::Args; use console::{style, Emoji}; +use self_update::get_target; use tokio::task::spawn_blocking; use super::SwarmdCommand; @@ -22,20 +25,42 @@ impl SwarmdCommand for UpdateArg { style("").bold().dim(), DELIVERY ))?; - let _ = spawn_blocking(|| { + let status = spawn_blocking(|| { + let release = self_update::backends::github::Update::configure() + .repo_owner("swarmd-io") + .repo_name("swarmd") + .bin_name("swarmd") + .current_version(self_update::cargo_crate_version!()) + .build()? + .get_latest_release()?; + + let last_version = release.version; + let target = get_target(); + let bin_path = format!("swarmd-v{last_version}-{target}/swarmd"); + let status = self_update::backends::github::Update::configure() .repo_owner("swarmd-io") .repo_name("swarmd") + .bin_path_in_archive(&bin_path) .bin_name("swarmd") - .show_download_progress(true) - .current_version("swarmd-v.0.1.9") + .show_download_progress(false) + .current_version(self_update::cargo_crate_version!()) + .show_output(false) + .no_confirm(true) + .bin_install_path(current_dir().unwrap()) .build()? .update()?; - println!("Update status: `{:?}`!", status); - Ok::<_, anyhow::Error>(()) + Ok::<_, anyhow::Error>(status) }) - .await; + .await??; + + env.println(format!( + "{} {}New version `{}` installed", + style("").bold().dim(), + DELIVERY, + style(status.version()).bold().cyan(), + ))?; Ok(()) } diff --git a/cli/src/infrastructure/mod.rs b/cli/src/infrastructure/mod.rs index c8ee35e..ded26f9 100644 --- a/cli/src/infrastructure/mod.rs +++ b/cli/src/infrastructure/mod.rs @@ -10,6 +10,8 @@ pub mod http_client; pub mod swarmd_client; +pub mod updater; + pub const NAME: &str = env!("CARGO_CRATE_NAME"); pub const VERSION: &str = concat!( env!("CARGO_PKG_VERSION"), diff --git a/cli/src/infrastructure/updater/mod.rs b/cli/src/infrastructure/updater/mod.rs new file mode 100644 index 0000000..ddef3a4 --- /dev/null +++ b/cli/src/infrastructure/updater/mod.rs @@ -0,0 +1,33 @@ +use self_update::update::Release; +use tokio::task::spawn_blocking; + +pub async fn get_last_release() -> anyhow::Result> { + let release = spawn_blocking(|| { + let release = self_update::backends::github::Update::configure() + .repo_owner("swarmd-io") + .repo_name("swarmd") + .bin_name("swarmd") + .current_version(self_update::cargo_crate_version!()) + .build()? + .get_latest_release()?; + + Ok::<_, anyhow::Error>(release) + }) + .await? + .ok(); + + Ok(release) +} + +pub async fn check_if_update_available() -> anyhow::Result> { + if let Some(release) = get_last_release().await? { + let current_version = self_update::cargo_crate_version!(); + if current_version == release.version { + Ok(None) + } else { + Ok(Some(release.version)) + } + } else { + Ok(None) + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index a5eb946..8c4415b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -2,11 +2,14 @@ mod application; mod domain; mod infrastructure; +use std::time::Duration; + use application::CliConfig; use application::SwarmdCommand; use clap::Parser; +use console::style; use domain::Env; -use infrastructure::Cfg; +use infrastructure::{updater::check_if_update_available, Cfg}; mod package; @@ -16,18 +19,52 @@ use swarmd_instruments::{debug, Instruments}; async fn main() -> anyhow::Result<()> { let release_name = release_name!(); let _instruments = Instruments::new(release_name)?; - debug!("Starting the CLI"); + let last_release = tokio::spawn(check_if_update_available()); + debug!("Read configuration"); let cfg = Cfg::from_env()?; debug!("Generate Env or Context"); let env = Env::try_from(cfg)?; + // TODO(@miaxos): add a caching mechanism to avoid fetching the version each time. + let _ = tokio::time::timeout(Duration::from_secs(1), async move { + if let Some(release) = last_release.await?? { + let line = format!( + "{} `{}`", + style("Current version").yellow().dim(), + style(self_update::cargo_crate_version!()).bold().cyan(), + ); + let line2 = format!( + "{} `{}` {}", + style("New version").yellow().dim(), + style(release).bold().cyan(), + style("available").bold().yellow().dim(), + ); + let line3 = format!( + "{} `{}`", + style("Please update by doing").yellow().dim(), + style("swarmd update").bold().magenta(), + ); + println!("-----------------"); + println!("{}", line); + println!("{}", line2); + println!("{}", line3); + println!("-----------------"); + } + + Ok::<_, anyhow::Error>(()) + }) + .await; + debug!("Read Arguments"); let config = CliConfig::parse(); config.execute(&env).await?; + + println!("blblbl"); + Ok(()) }