diff --git a/.forgejo/workflows/release-image.yml b/.forgejo/workflows/release-image.yml index 3eb84223..de009ad5 100644 --- a/.forgejo/workflows/release-image.yml +++ b/.forgejo/workflows/release-image.yml @@ -143,10 +143,10 @@ jobs: context: . file: "docker/Dockerfile" build-args: | - CONDUWUIT_VERSION_EXTRA=${{ env.COMMIT_SHORT_SHA }} - COMMIT_SHA=${{ github.sha }}) - REMOTE_URL=${{github.event.repository.html_url }} - REMOTE_COMMIT_URL=${{github.event.head_commit.url }} + GIT_COMMIT_HASH=${{ github.sha }}) + GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }}) + GIT_REMOTE_URL=${{github.event.repository.html_url }} + GIT_REMOTE_COMMIT_URL=${{github.event.head_commit.url }} platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} diff --git a/Cargo.lock b/Cargo.lock index 1b6c5d26..5f82384e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,6 +584,9 @@ name = "built" version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" +dependencies = [ + "cargo-lock", +] [[package]] name = "bumpalo" @@ -631,6 +634,19 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cargo-lock" +version = "10.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06acb4f71407ba205a07cb453211e0e6a67b21904e47f6ba1f9589e38f2e454" +dependencies = [ + "petgraph", + "semver", + "serde", + "toml", + "url", +] + [[package]] name = "cargo_toml" version = "0.21.0" @@ -856,6 +872,13 @@ dependencies = [ "tracing", ] +[[package]] +name = "conduwuit_build_metadata" +version = "0.5.0-rc.5" +dependencies = [ + "built", +] + [[package]] name = "conduwuit_core" version = "0.5.0-rc.5" @@ -870,6 +893,7 @@ dependencies = [ "checked_ops", "chrono", "clap", + "conduwuit_build_metadata", "conduwuit_macros", "const-str", "core_affinity", @@ -1019,6 +1043,7 @@ version = "0.5.0-rc.5" dependencies = [ "askama", "axum", + "conduwuit_build_metadata", "futures", "rand 0.8.5", "thiserror 2.0.12", @@ -1515,6 +1540,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.1" @@ -3132,6 +3163,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.8.0", +] + [[package]] name = "phf" version = "0.11.3" @@ -4127,6 +4168,9 @@ name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] [[package]] name = "sentry" diff --git a/Cargo.toml b/Cargo.toml index 44db4c4b..43cd3f4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -631,6 +631,12 @@ package = "conduwuit_web" path = "src/web" default-features = false + +[workspace.dependencies.conduwuit-build-metadata] +package = "conduwuit_build_metadata" +path = "src/build_metadata" +default-features = false + ############################################################################### # # Release profiles diff --git a/docker/Dockerfile b/docker/Dockerfile index 56f2d5fa..3029282f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -115,116 +115,26 @@ FROM toolchain AS builder # Get source COPY . . -# Conduwuit version info -ARG COMMIT_SHA= -ARG SHORT_COMMIT_SHA= -ARG REMOTE_URL= -ARG CONDUWUIT_VERSION_EXTRA= -ENV COMMIT_SHA=$COMMIT_SHA -ENV SHORT_COMMIT_SHA=$SHORT_COMMIT_SHA -ENV REMOTE_URL=$REMOTE_URL -ENV CONDUWUIT_VERSION_EXTRA=$CONDUWUIT_VERSION_EXTRA - -# Calculate version info from git if not provided via ARGs -# and write all relevant vars to /etc/environment -RUN <<'EOF' -set -e # Exit on error - -# Use temp variables to store calculated values -calculated_commit_sha="" -calculated_remote_url="" -calculated_version_extra="" - -# --- COMMIT_SHA --- -# Calculate COMMIT_SHA if ENV var (from ARG) is empty -if [ -z "${COMMIT_SHA}" ]; then - # Try to get short commit hash from git - calculated_commit_sha=$(git rev-parse HEAD 2>/dev/null || echo "") - if [ -n "${calculated_commit_sha}" ]; then - echo "COMMIT_SHA='${calculated_commit_sha}'" >> /etc/environment - fi -else - # Ensure ARG-provided value is in /etc/environment - echo "COMMIT_SHA='${COMMIT_SHA}'" >> /etc/environment -fi - - -if [ -z "${SHORT_COMMIT_SHA}" ]; then - # Try to get short commit hash from git - calculated_short_commit_sha=$(git rev-parse --short HEAD 2>/dev/null || echo "") - if [ -n "${calculated_short_commit_sha}" ]; then - echo "SHORT_COMMIT_SHA='${calculated_short_commit_sha}'" >> /etc/environment - fi -else - # Ensure ARG-provided value is in /etc/environment - echo "SHORT_COMMIT_SHA='${SHORT_COMMIT_SHA}'" >> /etc/environment -fi - -# --- REMOTE_URL --- -# Calculate REMOTE_URL if ENV var (from ARG) is empty -if [ -z "${REMOTE_URL}" ]; then - # Try to get remote origin URL from git - remote_url_raw=$(git config --get remote.origin.url 2>/dev/null || echo "") - if [ -n "${remote_url_raw}" ]; then - # Transform git URL (SSH or HTTPS) to web URL - if [[ $remote_url_raw == "https://"* ]]; then - # Already HTTPS, just remove .git suffix - calculated_remote_url=$(echo "$remote_url_raw" | sed 's/\.git$//') - else - # Convert SSH URL to HTTPS URL - calculated_remote_url=$(echo "$remote_url_raw" | sed 's/\.git$//' | sed 's/:/\//' | sed 's/^git@/https:\/\//') - fi - - # Write calculated web URL if transformation was successful - if [ -n "${calculated_remote_url}" ]; then - echo "REMOTE_URL='${calculated_remote_url}'" >> /etc/environment - fi - fi -else - # Ensure ARG-provided value is in /etc/environment (assume it's a valid web URL) - echo "REMOTE_URL='${REMOTE_URL}'" >> /etc/environment - # Use provided value for REMOTE_COMMIT_URL calculation below - calculated_remote_url="${REMOTE_URL}" -fi - -# --- Determine effective values for subsequent calculations --- -# Use ENV var value if set (from ARG), otherwise use calculated value -effective_commit_sha="${COMMIT_SHA:-$calculated_commit_sha}" -effective_short_commit_sha="${SHORT_COMMIT_SHA:-$calculated_short_commit_sha}" -effective_remote_url="${REMOTE_URL:-$calculated_remote_url}" - -# --- REMOTE_COMMIT_URL --- -# Calculate and write REMOTE_COMMIT_URL if both components are available -if [ -z "${REMOTE_COMMIT_URL}" ] && [ -n "${effective_remote_url}" ] && [ -n "${effective_commit_sha}" ]; then - echo "REMOTE_COMMIT_URL='${effective_remote_url}/commit/${effective_commit_sha}'" >> /etc/environment -else - # Ensure ARG-provided value is in /etc/environment - echo "REMOTE_COMMIT_URL='${REMOTE_COMMIT_URL}'" >> /etc/environment -fi - -# --- CONDUWUIT_VERSION_EXTRA --- -# Calculate CONDUWUIT_VERSION_EXTRA if ENV var (from ARG) is empty -if [ -z "${CONDUWUIT_VERSION_EXTRA}" ]; then - # Use the effective short commit sha, fallback to "unknown revision" - calculated_version_extra="${effective_short_commit_sha:-unknown revision}" - # Handle case where commit sha calculation failed and ARG wasn't set - if [ -z "${calculated_version_extra}" ]; then - calculated_version_extra="unknown revision" - fi - echo "CONDUWUIT_VERSION_EXTRA='${calculated_version_extra}'" >> /etc/environment -else - # Ensure ARG-provided value is in /etc/environment - echo "CONDUWUIT_VERSION_EXTRA='${CONDUWUIT_VERSION_EXTRA}'" >> /etc/environment -fi - -EOF - ARG TARGETPLATFORM # Verify environment configuration RUN cat /etc/environment RUN xx-cargo --print-target-triple +# Conduwuit version info +ARG GIT_COMMIT_HASH= +ARG GIT_COMMIT_HASH_SHORT= +ARG GIT_REMOTE_URL= +ARG GIT_REMOTE_COMMIT_URL= +ARG CONDUWUIT_VERSION_EXTRA= +ARG CONTINUWUITY_VERSION_EXTRA= +ENV GIT_COMMIT_HASH=$GIT_COMMIT_HASH +ENV GIT_COMMIT_HASH_SHORT=$GIT_COMMIT_HASH_SHORT +ENV GIT_REMOTE_URL=$GIT_REMOTE_URL +ENV GIT_REMOTE_COMMIT_URL=$GIT_REMOTE_COMMIT_URL +ENV CONDUWUIT_VERSION_EXTRA=$CONDUWUIT_VERSION_EXTRA +ENV CONTINUWUITY_VERSION_EXTRA=$CONTINUWUITY_VERSION_EXTRA + # Build the binary RUN --mount=type=cache,target=/usr/local/cargo/registry \ diff --git a/nix/pkgs/main/default.nix b/nix/pkgs/main/default.nix index 3a43af5a..f2fffec0 100644 --- a/nix/pkgs/main/default.nix +++ b/nix/pkgs/main/default.nix @@ -130,9 +130,8 @@ buildDepsOnlyEnv = }); buildPackageEnv = { - COMMIT_SHA = inputs.self.rev or inputs.self.dirtyRev or ""; - SHORT_COMMIT_SHA = inputs.self.shortRev or inputs.self.dirtyShortRev or ""; - CONDUWUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev or ""; + GIT_COMMIT_HASH = inputs.self.rev or inputs.self.dirtyRev or ""; + GIT_COMMIT_HASH_SHORT = inputs.self.shortRev or inputs.self.dirtyShortRev or ""; } // buildDepsOnlyEnv // { # Only needed in static stdenv because these are transitive dependencies of rocksdb CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS diff --git a/src/api/client/message.rs b/src/api/client/message.rs index 16b1796a..e442850b 100644 --- a/src/api/client/message.rs +++ b/src/api/client/message.rs @@ -143,7 +143,10 @@ pub(crate) async fn get_message_events_route( if let Some(registration) = body.appservice_info.as_ref() { <&DeviceId>::from(registration.registration.id.as_str()) } else { - panic!("No device_id provided and no appservice registration found, this should be unreachable"); + panic!( + "No device_id provided and no appservice registration found, this \ + should be unreachable" + ); }, }, room_id, diff --git a/src/build_metadata/Cargo.toml b/src/build_metadata/Cargo.toml new file mode 100644 index 00000000..3a98c6bf --- /dev/null +++ b/src/build_metadata/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "conduwuit_build_metadata" +categories.workspace = true +description.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true + + +build = "build.rs" +# [[bin]] +# path = "main.rs" +# name = "conduwuit_build_metadata" + +[lib] +path = "mod.rs" +crate-type = [ + "rlib", +# "dylib", +] + +[features] + + +[dependencies] + +[build-dependencies] +built = {version = "0.7", features = ["cargo-lock", "dependency-tree"]} + +[lints] +workspace = true diff --git a/src/build_metadata/build.rs b/src/build_metadata/build.rs new file mode 100644 index 00000000..2fec16a7 --- /dev/null +++ b/src/build_metadata/build.rs @@ -0,0 +1,92 @@ +use std::process::Command; + +fn run_git_command(args: &[&str]) -> Option { + Command::new("git") + .args(args) + .output() + .ok() + .filter(|output| output.status.success()) + .and_then(|output| String::from_utf8(output.stdout).ok()) + .map(|s| s.trim().to_owned()) + .filter(|s| !s.is_empty()) +} +fn get_env(env_var: &str) -> Option { + match std::env::var(env_var) { + | Ok(val) if !val.is_empty() => Some(val), + | _ => None, + } +} +fn main() { + // built gets the default crate from the workspace. Not sure if this is intended + // behavior, but it's what we want. + built::write_built_file().expect("Failed to acquire build-time information"); + + // --- Git Information --- + let mut commit_hash = None; + let mut commit_hash_short = None; + let mut remote_url_web = None; + + // Get full commit hash + if let Some(hash) = + get_env("GIT_COMMIT_HASH").or_else(|| run_git_command(&["rev-parse", "HEAD"])) + { + println!("cargo:rustc-env=GIT_COMMIT_HASH={hash}"); + commit_hash = Some(hash); + } + + // Get short commit hash + if let Some(short_hash) = get_env("GIT_COMMIT_HASH_SHORT") + .or_else(|| run_git_command(&["rev-parse", "--short", "HEAD"])) + { + println!("cargo:rustc-env=GIT_COMMIT_HASH_SHORT={short_hash}"); + commit_hash_short = Some(short_hash); + } + + // Get remote URL and convert to web URL + if let Some(remote_url_raw) = get_env("GIT_REMOTE_URL") + .or_else(|| run_git_command(&["config", "--get", "remote.origin.url"])) + { + println!("cargo:rustc-env=GIT_REMOTE_URL={remote_url_raw}"); + let web_url = if remote_url_raw.starts_with("https://") { + remote_url_raw.trim_end_matches(".git").to_owned() + } else if remote_url_raw.starts_with("git@") { + remote_url_raw + .trim_end_matches(".git") + .replacen(':', "/", 1) + .replacen("git@", "https://", 1) + } else if remote_url_raw.starts_with("ssh://") { + remote_url_raw + .trim_end_matches(".git") + .replacen("git@", "", 1) + .replacen("ssh:", "https:", 1) + } else { + // Assume it's already a web URL or unknown format + remote_url_raw + }; + println!("cargo:rustc-env=GIT_REMOTE_WEB_URL={web_url}"); + remote_url_web = Some(web_url); + } + + // Construct remote commit URL + if let Some(remote_commit_url) = get_env("GIT_REMOTE_COMMIT_URL") { + println!("cargo:rustc-env=GIT_REMOTE_COMMIT_URL={remote_commit_url}"); + } else if let (Some(base_url), Some(hash)) = + (&remote_url_web, commit_hash.as_ref().or(commit_hash_short.as_ref())) + { + let commit_page = format!("{base_url}/commit/{hash}"); + println!("cargo:rustc-env=GIT_REMOTE_COMMIT_URL={commit_page}"); + } + + // --- Rerun Triggers --- + // Rerun if the git HEAD changes + println!("cargo:rerun-if-changed=.git/HEAD"); + // Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch) + if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"]) { + println!("cargo:rerun-if-changed=.git/{ref_path}"); + } + + println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH"); + println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH_SHORT"); + println!("cargo:rerun-if-env-changed=GIT_REMOTE_URL"); + println!("cargo:rerun-if-env-changed=GIT_REMOTE_COMMIT_URL"); +} diff --git a/src/build_metadata/mod.rs b/src/build_metadata/mod.rs new file mode 100644 index 00000000..cf3364c1 --- /dev/null +++ b/src/build_metadata/mod.rs @@ -0,0 +1,23 @@ +pub mod built { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +pub static GIT_COMMIT_HASH: Option<&str> = option_env!("GIT_COMMIT_HASH"); + +pub static GIT_COMMIT_HASH_SHORT: Option<&str> = option_env!("GIT_COMMIT_HASH_SHORT"); + +// this would be a lot better if Option::or was const. +pub static VERSION_EXTRA: Option<&str> = + if let v @ Some(_) = option_env!("CONTINUWUITY_VERSION_EXTRA") { + v + } else if let v @ Some(_) = option_env!("CONDUWUIT_VERSION_EXTRA") { + v + } else if let v @ Some(_) = option_env!("CONDUIT_VERSION_EXTRA") { + v + } else { + GIT_COMMIT_HASH_SHORT + }; +pub static GIT_REMOTE_WEB_URL: Option<&str> = option_env!("GIT_REMOTE_WEB_URL"); +pub static GIT_REMOTE_COMMIT_URL: Option<&str> = option_env!("GIT_REMOTE_COMMIT_URL"); + +// TODO: Mark dirty builds within the version string diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index f42b049b..0c33c590 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -67,6 +67,7 @@ checked_ops.workspace = true chrono.workspace = true clap.workspace = true conduwuit-macros.workspace = true +conduwuit-build-metadata.workspace = true const-str.workspace = true core_affinity.workspace = true ctor.workspace = true diff --git a/src/core/info/version.rs b/src/core/info/version.rs index 6abb6e13..523c40a2 100644 --- a/src/core/info/version.rs +++ b/src/core/info/version.rs @@ -26,13 +26,6 @@ pub fn user_agent() -> &'static str { USER_AGENT.get_or_init(init_user_agent) } fn init_user_agent() -> String { format!("{}/{}", name(), version()) } fn init_version() -> String { - option_env!("CONDUWUIT_VERSION_EXTRA") - .or(option_env!("CONDUIT_VERSION_EXTRA")) - .map_or(SEMANTIC.to_owned(), |extra| { - if extra.is_empty() { - SEMANTIC.to_owned() - } else { - format!("{SEMANTIC} ({extra})") - } - }) + conduwuit_build_metadata::VERSION_EXTRA + .map_or(SEMANTIC.to_owned(), |extra| format!("{SEMANTIC} ({extra})")) } diff --git a/src/web/Cargo.toml b/src/web/Cargo.toml index 07dde0f7..8e03a338 100644 --- a/src/web/Cargo.toml +++ b/src/web/Cargo.toml @@ -20,6 +20,8 @@ crate-type = [ [dependencies] +conduwuit-build-metadata.workspace = true + askama = "0.14.0" axum.workspace = true diff --git a/src/web/mod.rs b/src/web/mod.rs index ddf13be4..520a9a34 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -5,6 +5,7 @@ use axum::{ response::{Html, IntoResponse, Response}, routing::get, }; +use conduwuit_build_metadata::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL, VERSION_EXTRA}; pub fn build() -> Router<()> { Router::new().route("/", get(index_handler)) } diff --git a/src/web/templates/_layout.html.j2 b/src/web/templates/_layout.html.j2 index a38745e1..fd0a5b29 100644 --- a/src/web/templates/_layout.html.j2 +++ b/src/web/templates/_layout.html.j2 @@ -18,8 +18,8 @@ {%~ block footer ~%}