Sprint C: author-filtered stats scan, release cadence + repo size, weekly digest
- src/stats/: OwnEmailMatcher (config emails + GH no-reply per account) + git-side --author filtering (cheap even on the pterodactyl fork's 4700+ commits); streak (current/longest, midnight-grace rule), 52-week heatmap, day/hour histograms — one scan feeds all of them, data layer only for now (visual Stats-tab redesign stays in Sprint D's mockup-first cycle) - src/stats/cadence.rs: release cadence (median tag gap, local tags, Tier A) - src/stats/size.rs + cache.rs: git-object vs working-tree size, persisted in new ~/.vulp/stats_cache.toml (max 1 sample/day), recorded only during [R]/vulp refresh — confirms vulp's own filter-repo cleanup (539MB->834KB) - src/digest.rs: WeeklyDigest struct + 3 renderers (Discord embed/markdown archive/plaintext); vulp digest [send discord|webhook <url>] CLI, webhook stored via existing auth token store (~/.vulp/.tokens, 0600), never logged; send respects --dry-run - TUI: [d] on Stats previews the digest in a Modal::Info before anything is ever sent - fix: list_contributors used --all (double-counts after fetching remotes), now --branches - config: stats_author_emails field (TUI-editable) - verified: streak/delta/top-repo/release math against a hand-built commit history, webhook validation+permissions, dry-run, real refresh (51 repos, vulp's own entry confirms the filter-repo win), TUI preview via pyte
This commit is contained in:
1 parent
d99660420f
commit
14ef7fb06c
20 files changed
+890
-3
No files matched your search
@@ -48,6 +48,11 @@ pub struct AppConfig {
|
||||
/// CLI commands refuse to touch unless `--force` is passed.
|
||||
#[serde(default)]
|
||||
pub protected_repos: Vec<String>,
|
||||
/// Author emails counted as "you" for streak/heatmap/digest stats.
|
||||
/// Empty = fall back to `git config user.email`, then `email`. GitHub's
|
||||
/// no-reply address for each configured account is always matched too.
|
||||
#[serde(default)]
|
||||
pub stats_author_emails: Vec<String>,
|
||||
}
|
||||
|
||||
fn default_license_kind() -> String { "mit".into() }
|
||||
@@ -87,6 +92,7 @@ impl Default for AppConfig {
|
||||
license_holder: String::new(),
|
||||
default_license: default_license_kind(),
|
||||
protected_repos: vec![],
|
||||
stats_author_emails: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,6 +119,7 @@ impl AppConfig {
|
||||
("license_holder", self.license_holder.clone()),
|
||||
("default_license", self.default_license.clone()),
|
||||
("protected_repos", self.protected_repos.join(", ")),
|
||||
("stats_author_emails", self.stats_author_emails.join(", ")),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -143,6 +150,13 @@ impl AppConfig {
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
"stats_author_emails" => {
|
||||
self.stats_author_emails = value
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
}
|
||||
"workspace_groups" => {
|
||||
self.workspace_groups = value
|
||||
.split(',')
|
||||
|
||||
Reference in new issue
Block a user