From ab6001f072c35d27ea6d6ac61ee0bbfa22659e5a Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Mon, 29 May 2023 15:28:11 +0200 Subject: [PATCH 1/3] Use the vergen crate to generate version information * Add depend on the `vergen` crate (only use the `build`, `git` and `gitcl` features) * Add the build script `build.rs` to setup the environment variables from the build system --- Cargo.lock | 18 ++++++++++++++++++ Cargo.toml | 3 +++ build.rs | 9 +++++++++ 3 files changed, 30 insertions(+) create mode 100644 build.rs diff --git a/Cargo.lock b/Cargo.lock index 927117c..31dc43e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + [[package]] name = "approx" version = "0.5.1" @@ -1847,6 +1853,7 @@ dependencies = [ "reqwest", "rocket", "thiserror", + "vergen", ] [[package]] @@ -2274,6 +2281,17 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vergen" +version = "8.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3c89c2c7e50f33e4d35527e5bf9c11d6d132226dbbd1753f0fbe9f19ef88c6" +dependencies = [ + "anyhow", + "rustversion", + "time 0.3.20", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 87c790b..858f72e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,9 @@ reqwest = { version = "0.11.9", features = ["json"] } rocket = { version = "0.5.0-rc.3", features = ["json"] } thiserror = "1.0.31" +[build-dependencies] +vergen = { version = "8.2.1", default-features = false, features = ["build", "git", "gitcl"] } + [dev-dependencies] assert_float_eq = "1.1.3" assert_matches = "1.5.0" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..a390899 --- /dev/null +++ b/build.rs @@ -0,0 +1,9 @@ +use std::error::Error; +use vergen::EmitBuilder; + +fn main() -> Result<(), Box> { + // Generate the `cargo:` instructions to fill the appropriate environment variables. + EmitBuilder::builder().all_build().all_git().emit()?; + + Ok(()) +} From 7c2b012e957f4d4274fbb5426aaea3cbfe6aa280 Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Mon, 29 May 2023 15:32:17 +0200 Subject: [PATCH 2/3] Print the version on lift off --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ad2875d..5f4e85c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,6 +166,15 @@ fn rocket(maps_handle: MapsHandle) -> Rocket { let _refresher = rocket::tokio::spawn(maps_refresher); }) })) + .attach(AdHoc::on_liftoff("Version", |_| { + Box::pin(async move { + let name = env!("CARGO_PKG_NAME"); + let version = env!("CARGO_PKG_VERSION"); + let git_sha = &env!("VERGEN_GIT_SHA")[0..7]; + + println!("🌁 Started {name} v{version} (git @{git_sha})"); + }) + })) } /// Sets up Rocket and the maps cache refresher task. From 38fb28c2489d6e84f5b05906dfbc9d4dcd6a8efd Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Mon, 29 May 2023 15:42:33 +0200 Subject: [PATCH 3/3] Add a /version API endpoint * Introduce the `VersionInfo` struct, build from the vergen environment variables * Add the `version` handler to construct and return the version info * Update the README --- README.md | 25 +++++++++++++++++++++++++ src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ac68da4..501d208 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,31 @@ an address fails or if the position is out of bounds of the map, nothing is returned (HTTP 404). If the maps cannot/have not been downloaded or cached yet, a service unavailable error is returned (HTTP 503). +## Version endpoint + +The `/version` endpoint provides information of the current version and build +of the service. This can be used to check if it needs to be updated. +Again, there is no path and no query parameters, just: + +```http +GET /version +``` + +### Version responses + +The response uses the JSON format and typically looks like this: + +```json +{ + "version": "0.2.7", + "timestamp": "2023-05-29T13:34:34.701323159Z", + "git_sha": "bb5962d", + "git_timestamp": "2023-05-29T15:32:17.000000000+02:00" +} +``` + +(Build and git information in example output may be out of date.) + ## License Sinoptik is licensed under the MIT license (see the `LICENSE` file or diff --git a/src/lib.rs b/src/lib.rs index 5f4e85c..4abdda1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ use rocket::fairing::AdHoc; use rocket::http::Status; use rocket::response::Responder; use rocket::serde::json::Json; +use rocket::serde::Serialize; use rocket::{get, routes, Build, Request, Rocket, State}; use self::forecast::{forecast, Forecast, Metric}; @@ -84,13 +85,41 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for Error { } } -/// Result type that defaults to [`Error`] as the default error type. -pub(crate) type Result = std::result::Result; - #[derive(Responder)] #[response(content_type = "image/png")] struct PngImageData(Vec); +/// Result type that defaults to [`Error`] as the default error type. +pub(crate) type Result = std::result::Result; + +/// The version information as JSON response. +#[derive(Debug, Serialize)] +#[serde(crate = "rocket::serde")] +struct VersionInfo { + /// The version of the build. + version: String, + + /// The timestamp of the build. + timestamp: String, + + /// The (most recent) git SHA used for the build. + git_sha: String, + + /// The timestamp of the last git commit used for the build. + git_timestamp: String, +} +impl VersionInfo { + /// Retrieves the version information from the environment variables. + fn new() -> Self { + Self { + version: String::from(env!("CARGO_PKG_VERSION")), + timestamp: String::from(env!("VERGEN_BUILD_TIMESTAMP")), + git_sha: String::from(&env!("VERGEN_GIT_SHA")[0..7]), + git_timestamp: String::from(env!("VERGEN_GIT_COMMIT_TIMESTAMP")), + } + } +} + /// Handler for retrieving the forecast for an address. #[get("/forecast?
&")] async fn forecast_address( @@ -150,6 +179,12 @@ async fn map_geo( image_data.map(PngImageData) } +/// Returns the version information. +#[get("/version", format = "application/json")] +async fn version() -> Result> { + Ok(Json(VersionInfo::new())) +} + /// Sets up Rocket. fn rocket(maps_handle: MapsHandle) -> Rocket { let maps_refresher = maps::run(Arc::clone(&maps_handle)); @@ -157,7 +192,13 @@ fn rocket(maps_handle: MapsHandle) -> Rocket { rocket::build() .mount( "/", - routes![forecast_address, forecast_geo, map_address, map_geo], + routes![ + forecast_address, + forecast_geo, + map_address, + map_geo, + version + ], ) .manage(maps_handle) .attach(AdHoc::on_liftoff("Maps refresher", |_| {