1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Logic for the `registry` command.
//!
//! This module provides commands for managing Zoi registries, including
//! initialization, metadata generation, and package/advisory management.
use anyhow::Result;
use clap::{Parser, Subcommand};
use colored::Colorize;
/// The root registry management command.
#[derive(Parser, Debug)]
pub struct RegistryCommand {
/// The specific registry subcommand to execute.
#[command(subcommand)]
pub command: RegistryCommands
}
/// Available registry subcommands.
#[derive(Subcommand, Debug)]
pub enum RegistryCommands {
/// Initialize a new Zoi registry
Init {
/// Path where the registry should be initialized
#[arg(default_value = ".")]
path: std::path::PathBuf
},
/// Generate metadata files (packages.json and advisories.json)
#[command(alias = "gen-meta")]
GenerateMetadata,
/// Check registry integrity and validate packages
#[command(aliases = ["lint", "audit"])]
Check,
/// Add a new package to the registry
#[command(alias = "add-pkg")]
AddPackage {
/// Name of the package to add
name: Option<String>,
/// Repository tier (e.g. community, main)
#[arg(long, short)]
repo: Option<String>
},
/// Add a new security advisory for a package
#[command(alias = "sec")]
AddAdvisory {
/// Package name to add an advisory for
package: Option<String>,
/// Repository tier (e.g. community, main)
#[arg(long, short)]
repo: Option<String>
},
/// Build a .zrepo snapshot of this registry, plus its zsync control file
#[command(alias = "snapshot")]
Zrepo {
/// Registry directory to snapshot. Defaults to the current directory
#[arg(default_value = ".")]
path: std::path::PathBuf,
/// Where to write the .zrepo. Defaults to <handle>.zrepo
#[arg(long, short = 'o')]
output: Option<std::path::PathBuf>,
/// Sign the snapshot with a PGP key from the Zoi keyring
#[arg(long)]
sign: Option<String>
}
}
/// Run the registry management command.
///
/// # Errors
///
/// This function returns an error if any of the underlying registry operations
/// (initialization, metadata generation, checking, adding packages/advisories,
/// or building a snapshot and its update patch) fail.
pub fn run(args: RegistryCommand) -> Result<()> {
let registry_root = std::path::Path::new(".");
match args.command {
RegistryCommands::Init { path } => crate::pkg::registry::init(&path),
RegistryCommands::GenerateMetadata => {
crate::pkg::registry::generate_metadata(registry_root)
}
RegistryCommands::Check => crate::pkg::registry::check(registry_root),
RegistryCommands::AddPackage { name, repo } => {
crate::pkg::registry::add_package(
registry_root,
name.as_deref(),
repo.as_deref()
)
}
RegistryCommands::AddAdvisory { package, repo } => {
crate::pkg::registry::add_advisory(
registry_root,
package.as_deref(),
repo.as_deref()
)
}
RegistryCommands::Zrepo { path, output, sign } => {
build_zrepo(&path, output.as_deref(), sign.as_deref())
}
}
}
/// Builds a `.zrepo` snapshot of a registry directory.
///
/// # Errors
///
/// Returns an error if the directory is not a registry or the snapshot cannot
/// be written.
fn build_zrepo(
path: &std::path::Path,
output: Option<&std::path::Path>,
sign: Option<&str>
) -> Result<()> {
let output = match output {
Some(explicit) => explicit.to_path_buf(),
None => default_snapshot_output(path)?
};
let stats = crate::pkg::sync::zrepo::create_zrepo(path, &output, sign)?;
println!(
"{} Created snapshot: {}",
"::".bold().green(),
output.display()
);
println!(" Files: {}", stats.file_count);
println!(
" Size: {} (uncompressed {})",
zoi_core::utils::format_bytes(stats.compressed_size),
zoi_core::utils::format_bytes(stats.tar_size)
);
println!(" SHA-256: {}", stats.sha256);
println!(
" Sidecars: .hash, .size{}",
if sign.is_some() { ", .sig" } else { "" }
);
println!(
"{} Publish the snapshot and its .zsync control file together at the \
'url' in the registry's 'zrepo' section, and point 'hash' at the \
.hash sidecar. Clients use the control file to transfer only the \
frames that changed.",
"::".bold().blue()
);
Ok(())
}
/// Derives a default snapshot filename from the registry's declared handle.
fn default_snapshot_output(
path: &std::path::Path
) -> Result<std::path::PathBuf> {
let handle = zoi_core::config::read_repo_config(path)?.name;
let handle = if handle.is_empty() {
"registry".to_string()
} else {
handle
};
Ok(path.join(format!("{handle}.zrepo")))
}