Add a new metadata.Metadata proto separate from config to keep track of things last binary version.

This commit is contained in:
Sebastian Jeltsch
2025-09-19 21:06:54 +02:00
parent 2922606d4b
commit 1d6209df70
6 changed files with 89 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ pub fn build_protos(proto_path: impl AsRef<Path>) -> Result<()> {
let proto_files = vec![
PathBuf::from(format!("{path}/config.proto")),
PathBuf::from(format!("{path}/config_api.proto")),
PathBuf::from(format!("{path}/metadata.proto")),
PathBuf::from(format!("{path}/vault.proto")),
];

View File

@@ -4,6 +4,7 @@ fn main() -> std::io::Result<()> {
trailbase_build::init_env_logger();
println!("cargo::rerun-if-changed=migrations");
println!("cargo::rerun-if-changed=proto");
trailbase_build::setup_version_info!();
trailbase_build::build_protos("./proto")?;

View File

@@ -0,0 +1,9 @@
syntax = "proto2";
package metadata;
message Metadata {
/// Last executed version of the `trail` binary. Can be used to detect
/// and react to binary changes across runs.
optional string last_executed_version = 1;
}

View File

@@ -6,6 +6,7 @@ pub mod app_state;
pub mod config;
pub mod constants;
pub mod logging;
pub mod metadata;
pub mod records;
pub mod util;

View File

@@ -0,0 +1,74 @@
use log::*;
use tokio::fs;
use crate::config::ConfigError;
use crate::data_dir::DataDir;
pub mod proto {
use lazy_static::lazy_static;
use prost_reflect::text_format::FormatOptions;
use prost_reflect::{DynamicMessage, MessageDescriptor, ReflectMessage};
use crate::DESCRIPTOR_POOL;
use crate::config::ConfigError;
include!(concat!(env!("OUT_DIR"), "/metadata.rs"));
lazy_static! {
static ref METADATA_DESCRIPTOR: MessageDescriptor = DESCRIPTOR_POOL
.get_message_by_name("metadata.Metadata")
.expect("infallible");
}
impl Metadata {
pub fn new_with_custom_defaults() -> Self {
let version_info = trailbase_build::get_version_info!();
return Self {
last_executed_version: version_info.version_tag,
};
}
pub fn from_text(text: &str) -> Result<Self, ConfigError> {
let dyn_config = DynamicMessage::parse_text_format(METADATA_DESCRIPTOR.clone(), text)?;
return Ok(dyn_config.transcode_to::<Self>()?);
}
pub fn to_text(&self) -> Result<String, ConfigError> {
const PREFACE: &str = "# Auto-generated metadata.Metadata textproto";
let text: String = self
.transcode_to_dynamic()
.to_text_format_with_options(&FormatOptions::new().pretty(true).expand_any(true));
return Ok(format!("{PREFACE}\n{text}"));
}
}
}
pub async fn load_or_init_metadata_textproto(
data_dir: &DataDir,
) -> Result<proto::Metadata, ConfigError> {
let metadata_path = data_dir.config_path().join(METADATA_FILENAME);
let config: proto::Metadata = match fs::read_to_string(&metadata_path).await {
Ok(contents) => proto::Metadata::from_text(&contents)?,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => {
warn!("Falling back to default config: {err}");
let config = proto::Metadata::new_with_custom_defaults();
debug!("Writing metadata: {metadata_path:?}");
fs::write(&metadata_path, config.to_text()?.as_bytes()).await?;
config
}
_ => {
return Err(err.into());
}
},
};
return Ok(config);
}
const METADATA_FILENAME: &str = "metadata.textproto";

View File

@@ -6,6 +6,7 @@ use crate::app_state::{AppState, AppStateArgs, build_objectstore};
use crate::auth::jwt::{JwtHelper, JwtHelperError};
use crate::config::load_or_init_config_textproto;
use crate::constants::USER_TABLE;
use crate::metadata::load_or_init_metadata_textproto;
use crate::rand::generate_random_string;
use crate::schema_metadata::SchemaMetadataCache;
use crate::server::DataDir;
@@ -90,6 +91,8 @@ pub async fn init_app_state(args: InitArgs) -> Result<(bool, AppState), InitErro
// Read config or write default one.
let config = load_or_init_config_textproto(&args.data_dir, &schema_metadata).await?;
let _metadata = load_or_init_metadata_textproto(&args.data_dir).await?;
debug!("Initializing JSON schemas from config");
trailbase_schema::registry::set_user_schemas(
config