From a502c307918f2f4006a77085c7b9183f53d83c56 Mon Sep 17 00:00:00 2001 From: naskya Date: Fri, 21 Jun 2024 04:27:03 +0900 Subject: [PATCH] automatically update to the latest revision --- README.md | 2 +- src/command/config.rs | 2 +- src/command/config/migrate.rs | 87 ++++++++++++++++++++++++- src/command/config/migrate/v20240701.rs | 24 ++++--- src/command/config/validate.rs | 24 +++++-- src/config/mod.rs | 6 +- 6 files changed, 122 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 0b7803d..f705361 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Please make sure to `cd` to the Firefish local repository before running these c [The admin note](https://firefish.dev/firefish/firefish/-/blob/main/docs/notice-for-admins.md) may tell you that you need to update the config files. In such a case, please execute the following command. ```sh -fishctl config migrate +fishctl config migrate ``` ### Validate the config files diff --git a/src/command/config.rs b/src/command/config.rs index e87ac6d..f77598a 100644 --- a/src/command/config.rs +++ b/src/command/config.rs @@ -9,7 +9,7 @@ use clap::Subcommand; #[derive(Subcommand)] pub(crate) enum Commands { /// Convert an old config file into the new format - Migrate { revision: Revision }, + Migrate { revision: Option }, /// Validate the config file Validate, } diff --git a/src/command/config/migrate.rs b/src/command/config/migrate.rs index 273a1cf..000e605 100644 --- a/src/command/config/migrate.rs +++ b/src/command/config/migrate.rs @@ -2,18 +2,99 @@ mod v20240701; -use crate::config::Revision; +use crate::config::{Revision, CLIENT_CONFIG_PATH, OLD_CONFIG_PATH, SERVER_CONFIG_PATH}; +use serde::Deserialize; +use std::{fs, io::Read, path::Path}; #[derive(thiserror::Error, Debug)] pub(crate) enum MigrationError { + #[error("failed to determine the current config revision ({0})")] + UnknownRevision(&'static str), + #[error(transparent)] + ReadFile(#[from] std::io::Error), + #[error(transparent)] + InvalidConfig(#[from] toml::de::Error), #[error(transparent)] V20240701(#[from] v20240701::Error), } -pub(super) async fn run(revision: Revision) -> Result<(), MigrationError> { +fn next_revision() -> Result, MigrationError> { + let old_config_exists = Path::new(OLD_CONFIG_PATH).is_file(); + let server_config_exists = Path::new(SERVER_CONFIG_PATH).is_file(); + let client_config_exists = Path::new(CLIENT_CONFIG_PATH).is_file(); + + if server_config_exists && !client_config_exists { + return Err(MigrationError::UnknownRevision( + "client config file does not exist", + )); + } + if !server_config_exists && client_config_exists { + return Err(MigrationError::UnknownRevision( + "server config file does not exist", + )); + } + if !old_config_exists && !server_config_exists && !client_config_exists { + return Err(MigrationError::UnknownRevision( + "config file does not exist", + )); + } + + if old_config_exists && !server_config_exists && !client_config_exists { + return Ok(Some(Revision::V20240701)); + } + + #[derive(Deserialize)] + struct Config { + config_revision: Revision, + } + + let mut buffer = String::new(); + + let mut server_toml = fs::File::open(SERVER_CONFIG_PATH)?; + server_toml.read_to_string(&mut buffer)?; + + let server_config_revision = toml::from_str::(&buffer)?.config_revision; + + let mut client_toml = fs::File::open(CLIENT_CONFIG_PATH)?; + client_toml.read_to_string(&mut buffer)?; + + let client_config_revision = toml::from_str::(&buffer)?.config_revision; + + if server_config_revision != client_config_revision { + return Err(MigrationError::UnknownRevision( + "server config revision and client config revision are different", + )); + } + + let next_revision = match server_config_revision { + Revision::V20240701 => None, + }; + + Ok(next_revision) +} + +async fn update_to_latest() -> Result<(), MigrationError> { + if next_revision()?.is_none() { + println!("Your config files are already up-to-date! (as of this fishctl release)"); + return Ok(()); + } + + while let Some(next_revision) = next_revision()? { + run_impl(next_revision).await?; + } + Ok(()) +} + +async fn run_impl(revision: Revision) -> Result<(), MigrationError> { match revision { Revision::V20240701 => v20240701::run().await?, } - Ok(()) } + +pub(super) async fn run(revision: Option) -> Result<(), MigrationError> { + match revision { + Some(revision) => run_impl(revision).await, + None => update_to_latest().await, + } +} diff --git a/src/command/config/migrate/v20240701.rs b/src/command/config/migrate/v20240701.rs index e501fd1..aa7c330 100644 --- a/src/command/config/migrate/v20240701.rs +++ b/src/command/config/migrate/v20240701.rs @@ -1,7 +1,9 @@ //! `config migrate 20240701` subcommand //! -use crate::config::{client, server, Revision}; +use crate::config::{ + client, server, Revision, CLIENT_CONFIG_PATH, OLD_CONFIG_PATH, SERVER_CONFIG_PATH, +}; use color_print::cprintln; use sqlx::{postgres::PgConnectOptions, ConnectOptions}; use std::{ @@ -15,7 +17,7 @@ use yaml_rust::{Yaml, YamlLoader}; #[derive(thiserror::Error, Debug)] pub(crate) enum Error { - #[error("failed to parse the old config file (.config/default.yml)")] + #[error("failed to parse the old config file ({})", OLD_CONFIG_PATH)] ReadYaml(#[from] ReadYamlConfigError), #[error(transparent)] WriteToml(#[from] WriteTomlConfigError), @@ -43,16 +45,16 @@ pub(crate) enum WriteTomlConfigError { ServerSer(#[source] toml::ser::Error), #[error("failed to serialize the new client config into TOML format")] ClientSer(#[source] toml::ser::Error), - #[error("failed to write to `config/server.toml`")] + #[error("failed to create {}", SERVER_CONFIG_PATH)] ServerWrite(#[source] io::Error), - #[error("failed to write to `config/client.toml`")] + #[error("failed to create {}", CLIENT_CONFIG_PATH)] ClientWrite(#[source] io::Error), - #[error("failed to create `config` directory")] + #[error("failed to create the config directory")] Mkdir(#[source] io::Error), } fn read_default_yml() -> Result, ReadYamlConfigError> { - let mut default_yml = fs::File::open(".config/default.yml")?; + let mut default_yml = fs::File::open(OLD_CONFIG_PATH)?; let mut buffer = String::new(); default_yml.read_to_string(&mut buffer)?; @@ -430,6 +432,8 @@ fn create_new_client_config(meta: Meta) -> Result { } pub(super) async fn run() -> Result<(), Error> { + println!("Updating the config revision to 20240701..."); + let (default_yml, meta) = read_old_config().await?; let server_config = create_new_server_config(default_yml, &meta)?; @@ -440,7 +444,7 @@ pub(super) async fn run() -> Result<(), Error> { } let mut server_toml = - fs::File::create("config/server.toml").map_err(WriteTomlConfigError::ServerWrite)?; + fs::File::create(SERVER_CONFIG_PATH).map_err(WriteTomlConfigError::ServerWrite)?; server_toml .write( toml::to_string_pretty(&server_config) @@ -448,10 +452,10 @@ pub(super) async fn run() -> Result<(), Error> { .as_bytes(), ) .map_err(WriteTomlConfigError::ServerWrite)?; - cprintln!("config/server.toml has been created!"); + cprintln!("{} has been created!", SERVER_CONFIG_PATH); let mut client_toml = - fs::File::create("config/client.toml").map_err(WriteTomlConfigError::ClientWrite)?; + fs::File::create(CLIENT_CONFIG_PATH).map_err(WriteTomlConfigError::ClientWrite)?; client_toml .write( toml::to_string_pretty(&client_config) @@ -459,7 +463,7 @@ pub(super) async fn run() -> Result<(), Error> { .as_bytes(), ) .map_err(WriteTomlConfigError::ClientWrite)?; - cprintln!("config/client.toml has been created!"); + cprintln!("{} has been created!", CLIENT_CONFIG_PATH); Ok(()) } diff --git a/src/command/config/validate.rs b/src/command/config/validate.rs index 4cad82f..fc1980e 100644 --- a/src/command/config/validate.rs +++ b/src/command/config/validate.rs @@ -1,6 +1,6 @@ //! `config validate` subcommand -use crate::config::{client, server}; +use crate::config::{client, server, CLIENT_CONFIG_PATH, SERVER_CONFIG_PATH}; use color_print::cprintln; use std::{fs, io::Read}; use validator::Validate; @@ -36,18 +36,30 @@ pub(super) fn run() -> Result<(), ValidationError> { let client_validation_result = match read_client_toml() { Ok(config) => config.validate().map_err(|err| { - cprintln!("config/client.toml is invalid.\n{}", err); + cprintln!( + "{} is invalid.\n{}", + CLIENT_CONFIG_PATH, + err + ); ValidationError::InvalidConfig }), Err(ReadError::InvalidFormat(err)) => { - cprintln!("config/client.toml is invalid.\n{}", err); + cprintln!( + "{} is invalid.\n{}", + CLIENT_CONFIG_PATH, + err + ); Err(ValidationError::InvalidConfig) } Err(ReadError::ReadFile(err)) => Err(ValidationError::ReadFile(err)), }; if server_validation_result.is_ok() && client_validation_result.is_ok() { - cprintln!("config/server.toml and config/client.toml are valid!"); + cprintln!( + "{} and {} are valid!", + SERVER_CONFIG_PATH, + CLIENT_CONFIG_PATH + ); cprintln!("Note: This is only a formal validation, so this does not guarantee that the settings are appropriate."); } @@ -55,7 +67,7 @@ pub(super) fn run() -> Result<(), ValidationError> { } fn read_server_toml() -> Result { - let mut file = fs::File::open("config/server.toml")?; + let mut file = fs::File::open(SERVER_CONFIG_PATH)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; @@ -64,7 +76,7 @@ fn read_server_toml() -> Result { } fn read_client_toml() -> Result { - let mut file = fs::File::open("config/client.toml")?; + let mut file = fs::File::open(CLIENT_CONFIG_PATH)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; diff --git a/src/config/mod.rs b/src/config/mod.rs index d59200f..e303dfb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -4,11 +4,13 @@ pub mod server; use clap::ValueEnum; use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Serialize, Clone, ValueEnum, Debug)] +#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, ValueEnum, Debug)] pub enum Revision { #[clap(name = "20240701")] #[serde(rename = "20240701")] V20240701, } -pub const LATEST_REVISION: Revision = Revision::V20240701; +pub const SERVER_CONFIG_PATH: &str = "config/server.toml"; +pub const CLIENT_CONFIG_PATH: &str = "config/client.toml"; +pub const OLD_CONFIG_PATH: &str = ".config/default.yml";