feat: add base dir to server
This commit is contained in:
@@ -52,6 +52,7 @@ host = "195.0.2.10" # алиас из ~/.ssh/config или IP
|
|||||||
port = 2222
|
port = 2222
|
||||||
user = "deploy"
|
user = "deploy"
|
||||||
key_path = "~/.ssh/id_ed25519"
|
key_path = "~/.ssh/id_ed25519"
|
||||||
|
base_dir = "/var/www/pages" # корень для относительных sync target
|
||||||
|
|
||||||
[projects.my-node-app]
|
[projects.my-node-app]
|
||||||
server = "my-vps" # ссылка на профиль
|
server = "my-vps" # ссылка на профиль
|
||||||
@@ -65,7 +66,8 @@ commands = ["npm ci", "npm run build"]
|
|||||||
|
|
||||||
[[projects.my-node-app.sync]] # что заливать на сервер
|
[[projects.my-node-app.sync]] # что заливать на сервер
|
||||||
source = "dist" # файл или папка (относительно workdir)
|
source = "dist" # файл или папка (относительно workdir)
|
||||||
target = "/var/www/pages"
|
target = "my-node-app" # относительный -> base_dir сервера;
|
||||||
|
# абсолютный "/path" используется как есть
|
||||||
|
|
||||||
[projects.my-node-app.remote] # команды управления на сервере
|
[projects.my-node-app.remote] # команды управления на сервере
|
||||||
commands = ["pm2 restart pages"]
|
commands = ["pm2 restart pages"]
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub struct Config {
|
|||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
|
pub base_dir: Option<String>,
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
pub key_path: Option<PathBuf>,
|
pub key_path: Option<PathBuf>,
|
||||||
@@ -115,6 +116,30 @@ impl Config {
|
|||||||
for rule in &mut project.sync {
|
for rule in &mut project.sync {
|
||||||
rule.source = expand_tilde(&rule.source)
|
rule.source = expand_tilde(&rule.source)
|
||||||
.with_context(|| format!("project '{name}': invalid sync source"))?;
|
.with_context(|| format!("project '{name}': invalid sync source"))?;
|
||||||
|
rule.target = expand_tilde(&rule.target)
|
||||||
|
.with_context(|| format!("project '{name}': invalid sync target"))?;
|
||||||
|
}
|
||||||
|
let base_dir = project
|
||||||
|
.server
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|s| servers.get(s))
|
||||||
|
.and_then(|s| s.base_dir.as_deref());
|
||||||
|
for rule in &mut project.sync {
|
||||||
|
if rule.target.to_string_lossy().starts_with('/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(base) = base_dir else {
|
||||||
|
bail!(
|
||||||
|
"project '{name}': sync target '{}' is relative, but the referenced server has no 'base_dir'",
|
||||||
|
rule.target.display()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let rel = rule.target.to_string_lossy().replace('\\', "/");
|
||||||
|
rule.target = PathBuf::from(format!(
|
||||||
|
"{}/{}",
|
||||||
|
base.trim_end_matches('/'),
|
||||||
|
rel.trim_start_matches('/')
|
||||||
|
));
|
||||||
}
|
}
|
||||||
for key in project.env.keys() {
|
for key in project.env.keys() {
|
||||||
let valid = !key.is_empty()
|
let valid = !key.is_empty()
|
||||||
@@ -292,6 +317,46 @@ workdir = "."
|
|||||||
assert!(err.to_string().contains("no host"), "{err:#}");
|
assert!(err.to_string().contains("no host"), "{err:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relative_sync_target_resolves_against_server_base_dir() {
|
||||||
|
let s = r#"
|
||||||
|
[servers.main]
|
||||||
|
host = "10.0.0.9"
|
||||||
|
base_dir = "/var/www/pages/"
|
||||||
|
|
||||||
|
[projects.web]
|
||||||
|
workdir = "."
|
||||||
|
server = "main"
|
||||||
|
|
||||||
|
[[projects.web.sync]]
|
||||||
|
source = "dist"
|
||||||
|
target = "web-app"
|
||||||
|
|
||||||
|
[[projects.web.sync]]
|
||||||
|
source = "dist/favicon.ico"
|
||||||
|
target = "/opt/static/favicon.ico"
|
||||||
|
"#;
|
||||||
|
let cfg = parse(s).unwrap();
|
||||||
|
let sync = &cfg.projects.get("web").unwrap().sync;
|
||||||
|
assert_eq!(sync[0].target.to_string_lossy(), "/var/www/pages/web-app");
|
||||||
|
assert_eq!(sync[1].target.to_string_lossy(), "/opt/static/favicon.ico");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relative_sync_target_without_base_dir_is_error() {
|
||||||
|
let s = r#"
|
||||||
|
[projects.web]
|
||||||
|
workdir = "."
|
||||||
|
host = "10.0.0.9"
|
||||||
|
|
||||||
|
[[projects.web.sync]]
|
||||||
|
source = "dist"
|
||||||
|
target = "web-app"
|
||||||
|
"#;
|
||||||
|
let err = parse(s).unwrap_err();
|
||||||
|
assert!(err.to_string().contains("base_dir"), "{err:#}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_commands_section_is_rejected() {
|
fn empty_commands_section_is_rejected() {
|
||||||
let s = r#"
|
let s = r#"
|
||||||
|
|||||||
Reference in New Issue
Block a user