| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | use std::io::Read as _; |
| 13 | use std::path::{Path, PathBuf}; |
| 14 | use std::process::ExitCode; |
| 15 | |
| 16 | use clap::Subcommand; |
| 17 | |
| 18 | use crate::context::{block_on, open_store}; |
| 19 | |
| 20 | |
| 21 | #[derive(Debug, Subcommand)] |
| 22 | pub(crate) enum HookCommand { |
| 23 | |
| 24 | PostReceive, |
| 25 | } |
| 26 | |
| 27 | |
| 28 | pub(crate) fn run(command: &HookCommand) -> ExitCode { |
| 29 | match command { |
| 30 | HookCommand::PostReceive => { |
| 31 | if let Err(err) = post_receive() { |
| 32 | |
| 33 | eprintln!("fabrica hook post-receive: {err:#}"); |
| 34 | } |
| 35 | ExitCode::SUCCESS |
| 36 | } |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | |
| 41 | fn post_receive() -> anyhow::Result<()> { |
| 42 | |
| 43 | |
| 44 | let mut input = String::new(); |
| 45 | std::io::stdin().read_to_string(&mut input)?; |
| 46 | |
| 47 | |
| 48 | |
| 49 | let git_dir = std::env::var_os("GIT_DIR").map_or_else(|| PathBuf::from("."), PathBuf::from); |
| 50 | let git_dir = std::fs::canonicalize(&git_dir).unwrap_or(git_dir); |
| 51 | |
| 52 | |
| 53 | let Some(repo_id) = git_dir |
| 54 | .file_name() |
| 55 | .and_then(|n| n.to_str()) |
| 56 | .and_then(|n| n.strip_suffix(".git")) |
| 57 | .filter(|id| id.len() == 26) |
| 58 | else { |
| 59 | |
| 60 | return Ok(()); |
| 61 | }; |
| 62 | let repo_id = repo_id.to_string(); |
| 63 | |
| 64 | let config_path = std::env::var_os("FABRICA_CONFIG").map(PathBuf::from); |
| 65 | let size = dir_size(&git_dir); |
| 66 | |
| 67 | block_on(async move { |
| 68 | let (config, store) = open_store(config_path.as_deref()).await?; |
| 69 | store.record_push(&repo_id, size).await?; |
| 70 | |
| 71 | mirror::trigger_push_mirrors(&store, &config, &repo_id).await; |
| 72 | anyhow::Ok(()) |
| 73 | }) |
| 74 | } |
| 75 | |
| 76 | |
| 77 | fn dir_size(dir: &Path) -> i64 { |
| 78 | fn walk(dir: &Path, total: &mut u64) { |
| 79 | let Ok(entries) = std::fs::read_dir(dir) else { |
| 80 | return; |
| 81 | }; |
| 82 | for entry in entries.flatten() { |
| 83 | let path = entry.path(); |
| 84 | match entry.file_type() { |
| 85 | Ok(ft) if ft.is_dir() => walk(&path, total), |
| 86 | Ok(ft) if ft.is_file() => { |
| 87 | if let Ok(meta) = entry.metadata() { |
| 88 | *total += meta.len(); |
| 89 | } |
| 90 | } |
| 91 | _ => {} |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | let mut total = 0u64; |
| 96 | walk(dir, &mut total); |
| 97 | i64::try_from(total).unwrap_or(i64::MAX) |
| 98 | } |