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.
|
[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
|
```sh
|
||||||
fishctl config migrate <revision>
|
fishctl config migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
### Validate the config files
|
### Validate the config files
|
||||||
|
|
|
@ -9,7 +9,7 @@ use clap::Subcommand;
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub(crate) enum Commands {
|
pub(crate) enum Commands {
|
||||||
/// Convert an old config file into the new format
|
/// Convert an old config file into the new format
|
||||||
Migrate { revision: Revision },
|
Migrate { revision: Option<Revision> },
|
||||||
/// Validate the config file
|
/// Validate the config file
|
||||||
Validate,
|
Validate,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,99 @@
|
||||||
|
|
||||||
mod v20240701;
|
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)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub(crate) enum MigrationError {
|
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)]
|
#[error(transparent)]
|
||||||
V20240701(#[from] v20240701::Error),
|
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 {
|
match revision {
|
||||||
Revision::V20240701 => v20240701::run().await?,
|
Revision::V20240701 => v20240701::run().await?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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
|
//! `config migrate 20240701` subcommand
|
||||||
//! <https://firefish.dev/firefish/firefish/-/issues/10947>
|
//! <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 color_print::cprintln;
|
||||||
use sqlx::{postgres::PgConnectOptions, ConnectOptions};
|
use sqlx::{postgres::PgConnectOptions, ConnectOptions};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -15,7 +17,7 @@ use yaml_rust::{Yaml, YamlLoader};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub(crate) enum Error {
|
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),
|
ReadYaml(#[from] ReadYamlConfigError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
WriteToml(#[from] WriteTomlConfigError),
|
WriteToml(#[from] WriteTomlConfigError),
|
||||||
|
@ -43,16 +45,16 @@ pub(crate) enum WriteTomlConfigError {
|
||||||
ServerSer(#[source] toml::ser::Error),
|
ServerSer(#[source] toml::ser::Error),
|
||||||
#[error("failed to serialize the new client config into TOML format")]
|
#[error("failed to serialize the new client config into TOML format")]
|
||||||
ClientSer(#[source] toml::ser::Error),
|
ClientSer(#[source] toml::ser::Error),
|
||||||
#[error("failed to write to `config/server.toml`")]
|
#[error("failed to create {}", SERVER_CONFIG_PATH)]
|
||||||
ServerWrite(#[source] io::Error),
|
ServerWrite(#[source] io::Error),
|
||||||
#[error("failed to write to `config/client.toml`")]
|
#[error("failed to create {}", CLIENT_CONFIG_PATH)]
|
||||||
ClientWrite(#[source] io::Error),
|
ClientWrite(#[source] io::Error),
|
||||||
#[error("failed to create `config` directory")]
|
#[error("failed to create the config directory")]
|
||||||
Mkdir(#[source] io::Error),
|
Mkdir(#[source] io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_default_yml() -> Result<HashMap<String, Yaml>, ReadYamlConfigError> {
|
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();
|
let mut buffer = String::new();
|
||||||
default_yml.read_to_string(&mut buffer)?;
|
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> {
|
pub(super) async fn run() -> Result<(), Error> {
|
||||||
|
println!("Updating the config revision to 20240701...");
|
||||||
|
|
||||||
let (default_yml, meta) = read_old_config().await?;
|
let (default_yml, meta) = read_old_config().await?;
|
||||||
|
|
||||||
let server_config = create_new_server_config(default_yml, &meta)?;
|
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 =
|
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
|
server_toml
|
||||||
.write(
|
.write(
|
||||||
toml::to_string_pretty(&server_config)
|
toml::to_string_pretty(&server_config)
|
||||||
|
@ -448,10 +452,10 @@ pub(super) async fn run() -> Result<(), Error> {
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
.map_err(WriteTomlConfigError::ServerWrite)?;
|
.map_err(WriteTomlConfigError::ServerWrite)?;
|
||||||
cprintln!("<bold>config/server.toml</> has been created!");
|
cprintln!("<bold>{}</> has been created!", SERVER_CONFIG_PATH);
|
||||||
|
|
||||||
let mut client_toml =
|
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
|
client_toml
|
||||||
.write(
|
.write(
|
||||||
toml::to_string_pretty(&client_config)
|
toml::to_string_pretty(&client_config)
|
||||||
|
@ -459,7 +463,7 @@ pub(super) async fn run() -> Result<(), Error> {
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
.map_err(WriteTomlConfigError::ClientWrite)?;
|
.map_err(WriteTomlConfigError::ClientWrite)?;
|
||||||
cprintln!("<bold>config/client.toml</> has been created!");
|
cprintln!("<bold>{}</> has been created!", CLIENT_CONFIG_PATH);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! `config validate` subcommand
|
//! `config validate` subcommand
|
||||||
|
|
||||||
use crate::config::{client, server};
|
use crate::config::{client, server, CLIENT_CONFIG_PATH, SERVER_CONFIG_PATH};
|
||||||
use color_print::cprintln;
|
use color_print::cprintln;
|
||||||
use std::{fs, io::Read};
|
use std::{fs, io::Read};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
@ -36,18 +36,30 @@ pub(super) fn run() -> Result<(), ValidationError> {
|
||||||
|
|
||||||
let client_validation_result = match read_client_toml() {
|
let client_validation_result = match read_client_toml() {
|
||||||
Ok(config) => config.validate().map_err(|err| {
|
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
|
ValidationError::InvalidConfig
|
||||||
}),
|
}),
|
||||||
Err(ReadError::InvalidFormat(err)) => {
|
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(ValidationError::InvalidConfig)
|
||||||
}
|
}
|
||||||
Err(ReadError::ReadFile(err)) => Err(ValidationError::ReadFile(err)),
|
Err(ReadError::ReadFile(err)) => Err(ValidationError::ReadFile(err)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if server_validation_result.is_ok() && client_validation_result.is_ok() {
|
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.");
|
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> {
|
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();
|
let mut buffer = String::new();
|
||||||
file.read_to_string(&mut buffer)?;
|
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> {
|
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();
|
let mut buffer = String::new();
|
||||||
file.read_to_string(&mut buffer)?;
|
file.read_to_string(&mut buffer)?;
|
||||||
|
|
|
@ -4,11 +4,13 @@ pub mod server;
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, ValueEnum, Debug)]
|
#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone, ValueEnum, Debug)]
|
||||||
pub enum Revision {
|
pub enum Revision {
|
||||||
#[clap(name = "20240701")]
|
#[clap(name = "20240701")]
|
||||||
#[serde(rename = "20240701")]
|
#[serde(rename = "20240701")]
|
||||||
V20240701,
|
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