feat: deploy drift via auto-discovered VPS mirror, no manual metadata
Sprint E, redesigned from the original per-repo-metadata plan: the deploy host already mirrors workspace_root 1:1 (documented convention), so instead of maintaining a repo->host/compose-path mapping by hand, vulp refresh runs one batched remote command (via a configurable ssh wrapper) listing every mirrored repo's HEAD sha and diffs it against local HEAD. - src/deploy.rs: DeployState (InSync/Behind(n)/Diverged), scan_deploy_heads (one remote round-trip, retried once for this VPS's known SSH flakiness), drift_for (path relative to workspace_root -> host lookup -> local git rev-list --count) - config: deploy_ssh_cmd + deploy_root, both empty by default (feature is fully inert, no SSH attempted, until configured) - cache: RepoMeta.deploy_status, folded into the existing refresh loop (TUI [R] and CLI) alongside CI/dependabot rather than a second pass - table: DEPLOY column (checkmark/behind-count/diverged/dash); CLI list --stale-deploy and deploy_status in --json - verified against the real VPS: 37/49 repos matched to a mirror (30 in sync, 7 behind 1-4 commits - a real signal), everything else correctly shows unset (no mirror there, e.g. vulp itself, a pure OSS dev tool) - set deploy_ssh_cmd/deploy_root in the live config so this is active now, not just theoretically configurable
This commit is contained in:
1 parent
a3658d5e97
commit
4b0a6e324f
15 files changed
+256
-16
No files matched your search
@@ -53,6 +53,15 @@ pub struct AppConfig {
|
||||
/// no-reply address for each configured account is always matched too.
|
||||
#[serde(default)]
|
||||
pub stats_author_emails: Vec<String>,
|
||||
/// Command (program + args, split on whitespace) that runs its final
|
||||
/// argument as a shell command on the deploy host and prints stdout —
|
||||
/// e.g. a wrapper around `ssh`. Empty = deploy-drift feature disabled.
|
||||
#[serde(default)]
|
||||
pub deploy_ssh_cmd: String,
|
||||
/// Root on the deploy host that mirrors `workspace_root` 1:1 (relative
|
||||
/// paths past these two roots are identical). Empty = feature disabled.
|
||||
#[serde(default)]
|
||||
pub deploy_root: String,
|
||||
}
|
||||
|
||||
fn default_license_kind() -> String { "mit".into() }
|
||||
@@ -93,6 +102,8 @@ impl Default for AppConfig {
|
||||
default_license: default_license_kind(),
|
||||
protected_repos: vec![],
|
||||
stats_author_emails: vec![],
|
||||
deploy_ssh_cmd: String::new(),
|
||||
deploy_root: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +131,8 @@ impl AppConfig {
|
||||
("default_license", self.default_license.clone()),
|
||||
("protected_repos", self.protected_repos.join(", ")),
|
||||
("stats_author_emails", self.stats_author_emails.join(", ")),
|
||||
("deploy_ssh_cmd", self.deploy_ssh_cmd.clone()),
|
||||
("deploy_root", self.deploy_root.clone()),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -157,6 +170,8 @@ impl AppConfig {
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
"deploy_ssh_cmd" => self.deploy_ssh_cmd = value,
|
||||
"deploy_root" => self.deploy_root = value,
|
||||
"workspace_groups" => {
|
||||
self.workspace_groups = value
|
||||
.split(',')
|
||||
|
||||
Reference in new issue
Block a user