automatically update to the latest revision
This commit is contained in:
parent
6e908aebdb
commit
a502c30791
6 changed files with 122 additions and 23 deletions
|
@ -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 <revision>
|
||||
fishctl config migrate
|
||||
```
|
||||
|
||||
### Validate the config files
|
||||
|
|
|
@ -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<Revision> },
|
||||
/// Validate the config file
|
||||
Validate,
|
||||
}
|
||||
|
|
|
@ -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<Option<Revision>, 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::<Config>(&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::<Config>(&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<Revision>) -> Result<(), MigrationError> {
|
||||
match revision {
|
||||
Some(revision) => run_impl(revision).await,
|
||||
None => update_to_latest().await,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! `config migrate 20240701` subcommand
|
||||
//! <https://firefish.dev/firefish/firefish/-/issues/10947>
|
||||
|
||||
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<HashMap<String, Yaml>, 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<client::Config, Error> {
|
|||
}
|
||||
|
||||
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!("<bold>config/server.toml</> has been created!");
|
||||
cprintln!("<bold>{}</> 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!("<bold>config/client.toml</> has been created!");
|
||||
cprintln!("<bold>{}</> has been created!", CLIENT_CONFIG_PATH);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -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!("<r!><bold>config/client.toml is invalid.</></>\n{}", err);
|
||||
cprintln!(
|
||||
"<r!><bold>{} is invalid.</></>\n{}",
|
||||
CLIENT_CONFIG_PATH,
|
||||
err
|
||||
);
|
||||
ValidationError::InvalidConfig
|
||||
}),
|
||||
Err(ReadError::InvalidFormat(err)) => {
|
||||
cprintln!("<r!><bold>config/client.toml is invalid.</></>\n{}", err);
|
||||
cprintln!(
|
||||
"<r!><bold>{} 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!("<g!><bold>config/server.toml and config/client.toml are valid!</></>");
|
||||
cprintln!(
|
||||
"<g!><bold>{} and {} are valid!</></>",
|
||||
SERVER_CONFIG_PATH,
|
||||
CLIENT_CONFIG_PATH
|
||||
);
|
||||
cprintln!("<bold>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<server::Config, ReadError> {
|
||||
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<server::Config, ReadError> {
|
|||
}
|
||||
|
||||
fn read_client_toml() -> Result<client::Config, ReadError> {
|
||||
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)?;
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in a new issue