docs: added documentation, enforced documentation
All checks were successful
CI / build (push) Successful in 7m21s
All checks were successful
CI / build (push) Successful in 7m21s
This commit is contained in:
@@ -2,6 +2,7 @@ use std::error::Error;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
/// Build a Debian source package (to a .dsc)
|
||||||
pub fn build_source_package(cwd: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
pub fn build_source_package(cwd: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||||
let cwd = cwd.unwrap_or_else(|| Path::new("."));
|
let cwd = cwd.unwrap_or_else(|| Path::new("."));
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ use std::fs::File;
|
|||||||
use std::io::{self, BufRead, Read, Write};
|
use std::io::{self, BufRead, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/*
|
/// Automatically generate a changelog entry from a commit history and previous changelog
|
||||||
* Automatically generate a changelog entry from a commit history and previous changelog
|
|
||||||
*/
|
|
||||||
pub fn generate_entry(
|
pub fn generate_entry(
|
||||||
changelog_file: &str,
|
changelog_file: &str,
|
||||||
cwd: Option<&Path>,
|
cwd: Option<&Path>,
|
||||||
@@ -61,10 +59,8 @@ pub fn generate_entry(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Compute the next (most probable) version number of a package, from old version and
|
||||||
* Compute the next (most probable) version number of a package, from old version and
|
/// conditions on changes (is ubuntu upload, is a no change rebuild, is a non-maintainer upload)
|
||||||
* conditions on changes (is ubuntu upload, is a no change rebuild, is a non-maintainer upload)
|
|
||||||
*/
|
|
||||||
fn compute_new_version(
|
fn compute_new_version(
|
||||||
old_version: &str,
|
old_version: &str,
|
||||||
is_ubuntu: bool,
|
is_ubuntu: bool,
|
||||||
@@ -87,9 +83,7 @@ fn compute_new_version(
|
|||||||
increment_suffix(old_version, "")
|
increment_suffix(old_version, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Increment a version number by 1, for a given suffix
|
||||||
* Increment a version number by 1, for a given suffix
|
|
||||||
*/
|
|
||||||
fn increment_suffix(version: &str, suffix: &str) -> String {
|
fn increment_suffix(version: &str, suffix: &str) -> String {
|
||||||
// If suffix is empty, we just look for trailing digits
|
// If suffix is empty, we just look for trailing digits
|
||||||
// If suffix is not empty, we look for suffix followed by digits
|
// If suffix is not empty, we look for suffix followed by digits
|
||||||
@@ -120,9 +114,7 @@ fn increment_suffix(version: &str, suffix: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Parse a changelog file first entry header, to obtain (package, version, series)
|
||||||
* Parse a changelog file first entry header, to obtain (package, version, series)
|
|
||||||
*/
|
|
||||||
pub fn parse_changelog_header(
|
pub fn parse_changelog_header(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<(String, String, String), Box<dyn std::error::Error>> {
|
) -> Result<(String, String, String), Box<dyn std::error::Error>> {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use super::schroot::SchrootDriver;
|
|||||||
use super::ssh::SshDriver;
|
use super::ssh::SshDriver;
|
||||||
use super::unshare::UnshareDriver;
|
use super::unshare::UnshareDriver;
|
||||||
|
|
||||||
|
/// A ContextDriver is the interface for the logic happening inside a context
|
||||||
pub trait ContextDriver {
|
pub trait ContextDriver {
|
||||||
fn ensure_available(&self, src: &Path, dest_root: &str) -> io::Result<PathBuf>;
|
fn ensure_available(&self, src: &Path, dest_root: &str) -> io::Result<PathBuf>;
|
||||||
fn retrieve_path(&self, src: &Path, dest: &Path) -> io::Result<()>;
|
fn retrieve_path(&self, src: &Path, dest: &Path) -> io::Result<()>;
|
||||||
@@ -41,34 +42,52 @@ pub trait ContextDriver {
|
|||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub enum ContextConfig {
|
pub enum ContextConfig {
|
||||||
|
/// Local context: actions executed locally
|
||||||
#[serde(rename = "local")]
|
#[serde(rename = "local")]
|
||||||
#[default]
|
#[default]
|
||||||
Local,
|
Local,
|
||||||
|
/// SSH context: actions over an SSH connection
|
||||||
#[serde(rename = "ssh")]
|
#[serde(rename = "ssh")]
|
||||||
Ssh {
|
Ssh {
|
||||||
|
/// Host for the SSH connection
|
||||||
host: String,
|
host: String,
|
||||||
|
/// User for the SSH connection
|
||||||
user: Option<String>,
|
user: Option<String>,
|
||||||
|
/// TCP port for the SSH connection
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
},
|
},
|
||||||
|
/// Schroot context: using `schroot`
|
||||||
#[serde(rename = "schroot")]
|
#[serde(rename = "schroot")]
|
||||||
Schroot {
|
Schroot {
|
||||||
|
/// Name of the schroot
|
||||||
name: String,
|
name: String,
|
||||||
|
/// Optional parent context for the Schroot context
|
||||||
parent: Option<String>,
|
parent: Option<String>,
|
||||||
},
|
},
|
||||||
|
/// Unshare context: chroot with dropped permissions (using `unshare`)
|
||||||
#[serde(rename = "unshare")]
|
#[serde(rename = "unshare")]
|
||||||
Unshare {
|
Unshare {
|
||||||
|
/// Path to use for chrooting
|
||||||
path: String,
|
path: String,
|
||||||
|
/// Optional parent context for the Unshare context
|
||||||
parent: Option<String>,
|
parent: Option<String>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A context, allowing to run commands, read and write files, etc
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
/// Configuration for the context
|
||||||
pub config: ContextConfig,
|
pub config: ContextConfig,
|
||||||
|
/// Parent context for the context
|
||||||
|
///
|
||||||
|
/// For example, you could have a chroot context over an ssh connection
|
||||||
pub parent: Option<Arc<Context>>,
|
pub parent: Option<Arc<Context>>,
|
||||||
|
/// ContextDriver for the context, implementing the logic for actions
|
||||||
driver: Mutex<Option<Box<dyn ContextDriver + Send + Sync>>>,
|
driver: Mutex<Option<Box<dyn ContextDriver + Send + Sync>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
/// Create a context from configuration
|
||||||
pub fn new(config: ContextConfig) -> Self {
|
pub fn new(config: ContextConfig) -> Self {
|
||||||
let parent = match &config {
|
let parent = match &config {
|
||||||
ContextConfig::Schroot {
|
ContextConfig::Schroot {
|
||||||
@@ -97,6 +116,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a context with an explicit parent context
|
||||||
pub fn with_parent(config: ContextConfig, parent: Arc<Context>) -> Self {
|
pub fn with_parent(config: ContextConfig, parent: Arc<Context>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
@@ -105,6 +125,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a command inside context
|
||||||
pub fn command<S: AsRef<OsStr>>(&self, program: S) -> ContextCommand<'_> {
|
pub fn command<S: AsRef<OsStr>>(&self, program: S) -> ContextCommand<'_> {
|
||||||
ContextCommand {
|
ContextCommand {
|
||||||
context: self,
|
context: self,
|
||||||
@@ -126,6 +147,7 @@ impl Context {
|
|||||||
.ensure_available(src, dest_root)
|
.ensure_available(src, dest_root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a temp directory inside context
|
||||||
pub fn create_temp_dir(&self) -> io::Result<String> {
|
pub fn create_temp_dir(&self) -> io::Result<String> {
|
||||||
self.driver().as_ref().unwrap().create_temp_dir()
|
self.driver().as_ref().unwrap().create_temp_dir()
|
||||||
}
|
}
|
||||||
@@ -143,18 +165,22 @@ impl Context {
|
|||||||
self.driver().as_ref().unwrap().list_files(path)
|
self.driver().as_ref().unwrap().list_files(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy a path inside context
|
||||||
pub fn copy_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
|
pub fn copy_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
|
||||||
self.driver().as_ref().unwrap().copy_path(src, dest)
|
self.driver().as_ref().unwrap().copy_path(src, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a file inside context
|
||||||
pub fn read_file(&self, path: &Path) -> io::Result<String> {
|
pub fn read_file(&self, path: &Path) -> io::Result<String> {
|
||||||
self.driver().as_ref().unwrap().read_file(path)
|
self.driver().as_ref().unwrap().read_file(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a file inside context
|
||||||
pub fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
|
pub fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
|
||||||
self.driver().as_ref().unwrap().write_file(path, content)
|
self.driver().as_ref().unwrap().write_file(path, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create and obtain a specific driver for the context
|
||||||
pub fn driver(
|
pub fn driver(
|
||||||
&self,
|
&self,
|
||||||
) -> std::sync::MutexGuard<'_, Option<Box<dyn ContextDriver + Send + Sync>>> {
|
) -> std::sync::MutexGuard<'_, Option<Box<dyn ContextDriver + Send + Sync>>> {
|
||||||
@@ -182,6 +208,7 @@ impl Context {
|
|||||||
driver_lock
|
driver_lock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone a context
|
||||||
pub fn clone_raw(&self) -> Self {
|
pub fn clone_raw(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config: self.config.clone(),
|
config: self.config.clone(),
|
||||||
@@ -207,12 +234,13 @@ pub struct ContextCommand<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContextCommand<'a> {
|
impl<'a> ContextCommand<'a> {
|
||||||
|
/// Add an argument to current command
|
||||||
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
|
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
|
||||||
self.args.push(arg.as_ref().to_string_lossy().to_string());
|
self.args.push(arg.as_ref().to_string_lossy().to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support chaining args
|
/// Add multiple command arguments
|
||||||
pub fn args<I, S>(&mut self, args: I) -> &mut Self
|
pub fn args<I, S>(&mut self, args: I) -> &mut Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
@@ -224,6 +252,7 @@ impl<'a> ContextCommand<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set environment variable for command
|
||||||
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
|
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
|
||||||
where
|
where
|
||||||
K: AsRef<OsStr>,
|
K: AsRef<OsStr>,
|
||||||
@@ -236,6 +265,7 @@ impl<'a> ContextCommand<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set multiple environment variables for command
|
||||||
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
|
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (K, V)>,
|
I: IntoIterator<Item = (K, V)>,
|
||||||
@@ -248,11 +278,13 @@ impl<'a> ContextCommand<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set current working directory for command
|
||||||
pub fn current_dir<P: AsRef<OsStr>>(&mut self, dir: P) -> &mut Self {
|
pub fn current_dir<P: AsRef<OsStr>>(&mut self, dir: P) -> &mut Self {
|
||||||
self.cwd = Some(dir.as_ref().to_string_lossy().to_string());
|
self.cwd = Some(dir.as_ref().to_string_lossy().to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run command and obtain exit status
|
||||||
pub fn status(&mut self) -> io::Result<std::process::ExitStatus> {
|
pub fn status(&mut self) -> io::Result<std::process::ExitStatus> {
|
||||||
self.context.driver().as_ref().unwrap().run(
|
self.context.driver().as_ref().unwrap().run(
|
||||||
&self.program,
|
&self.program,
|
||||||
@@ -262,7 +294,7 @@ impl<'a> ContextCommand<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture output
|
/// Run command, capturing output
|
||||||
pub fn output(&mut self) -> io::Result<std::process::Output> {
|
pub fn output(&mut self) -> io::Result<std::process::Output> {
|
||||||
self.context.driver().as_ref().unwrap().run_output(
|
self.context.driver().as_ref().unwrap().run_output(
|
||||||
&self.program,
|
&self.program,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper managing contexts
|
||||||
pub struct ContextManager {
|
pub struct ContextManager {
|
||||||
context: RwLock<Arc<Context>>,
|
context: RwLock<Arc<Context>>,
|
||||||
config_path: PathBuf,
|
config_path: PathBuf,
|
||||||
@@ -67,10 +68,12 @@ impl ContextManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain current ContextManager configuration
|
||||||
pub fn get_config(&self) -> std::sync::RwLockReadGuard<'_, Config> {
|
pub fn get_config(&self) -> std::sync::RwLockReadGuard<'_, Config> {
|
||||||
self.config.read().unwrap()
|
self.config.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a ContextManager using a specific configuration path
|
||||||
pub fn with_path(path: PathBuf) -> Self {
|
pub fn with_path(path: PathBuf) -> Self {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
Self {
|
Self {
|
||||||
@@ -80,6 +83,7 @@ impl ContextManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Save current context configuration to disk
|
||||||
pub fn save(&self) -> io::Result<()> {
|
pub fn save(&self) -> io::Result<()> {
|
||||||
let config = self.config.read().unwrap();
|
let config = self.config.read().unwrap();
|
||||||
let content = serde_json::to_string_pretty(&*config)
|
let content = serde_json::to_string_pretty(&*config)
|
||||||
@@ -97,6 +101,7 @@ impl ContextManager {
|
|||||||
Context::new(context_config)
|
Context::new(context_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List contexts from configuration
|
||||||
pub fn list_contexts(&self) -> Vec<String> {
|
pub fn list_contexts(&self) -> Vec<String> {
|
||||||
self.config
|
self.config
|
||||||
.read()
|
.read()
|
||||||
@@ -107,6 +112,7 @@ impl ContextManager {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a context to configuration
|
||||||
pub fn add_context(&self, name: &str, config: ContextConfig) -> io::Result<()> {
|
pub fn add_context(&self, name: &str, config: ContextConfig) -> io::Result<()> {
|
||||||
self.config
|
self.config
|
||||||
.write()
|
.write()
|
||||||
@@ -116,6 +122,7 @@ impl ContextManager {
|
|||||||
self.save()
|
self.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove context from configuration
|
||||||
pub fn remove_context(&self, name: &str) -> io::Result<()> {
|
pub fn remove_context(&self, name: &str) -> io::Result<()> {
|
||||||
let mut config = self.config.write().unwrap();
|
let mut config = self.config.write().unwrap();
|
||||||
if name == "local" {
|
if name == "local" {
|
||||||
@@ -137,6 +144,7 @@ impl ContextManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set current context from name (modifying configuration)
|
||||||
pub fn set_current(&self, name: &str) -> io::Result<()> {
|
pub fn set_current(&self, name: &str) -> io::Result<()> {
|
||||||
let mut config = self.config.write().unwrap();
|
let mut config = self.config.write().unwrap();
|
||||||
if config.contexts.contains_key(name) {
|
if config.contexts.contains_key(name) {
|
||||||
@@ -153,14 +161,18 @@ impl ContextManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set current context, without modifying configuration
|
||||||
pub fn set_current_ephemeral(&self, context: Context) {
|
pub fn set_current_ephemeral(&self, context: Context) {
|
||||||
*self.context.write().unwrap() = context.into();
|
*self.context.write().unwrap() = context.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain current context handle
|
||||||
pub fn current(&self) -> Arc<Context> {
|
pub fn current(&self) -> Arc<Context> {
|
||||||
self.context.read().unwrap().clone()
|
self.context.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain current context name
|
||||||
|
/// Will not work for ephemeral context (obtained from config)
|
||||||
pub fn current_name(&self) -> String {
|
pub fn current_name(&self) -> String {
|
||||||
self.config.read().unwrap().context.clone()
|
self.config.read().unwrap().context.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ pub use api::{Context, ContextCommand, ContextConfig};
|
|||||||
pub use manager::ContextManager;
|
pub use manager::ContextManager;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Obtain global context manager
|
||||||
pub fn manager() -> &'static ContextManager {
|
pub fn manager() -> &'static ContextManager {
|
||||||
&manager::MANAGER
|
&manager::MANAGER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain current context
|
||||||
pub fn current() -> Arc<Context> {
|
pub fn current() -> Arc<Context> {
|
||||||
manager::MANAGER.current()
|
manager::MANAGER.current()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,16 @@ use crate::context;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Build mode for the binary build
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum BuildMode {
|
pub enum BuildMode {
|
||||||
|
/// Use `sbuild` for the build, configured in unshare mode
|
||||||
Sbuild,
|
Sbuild,
|
||||||
|
/// Local build, directly on the context
|
||||||
Local,
|
Local,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build package in 'cwd' to a .deb
|
||||||
pub fn build_binary_package(
|
pub fn build_binary_package(
|
||||||
arch: Option<&str>,
|
arch: Option<&str>,
|
||||||
series: Option<&str>,
|
series: Option<&str>,
|
||||||
|
|||||||
19
src/lib.rs
19
src/lib.rs
@@ -1,10 +1,27 @@
|
|||||||
|
//! pkh: Debian packaging helper
|
||||||
|
//!
|
||||||
|
//! pkh allows working with Debian packages, with multiple actions/submodules
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
/// Build a Debian source package (into a .dsc)
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
/// Parse or edit a Debian changelog of a source package
|
||||||
pub mod changelog;
|
pub mod changelog;
|
||||||
pub mod context;
|
/// Build a Debian package into a binary (.deb)
|
||||||
pub mod deb;
|
pub mod deb;
|
||||||
|
/// Obtain information about one or multiple packages
|
||||||
pub mod package_info;
|
pub mod package_info;
|
||||||
|
/// Download a source package locally
|
||||||
pub mod pull;
|
pub mod pull;
|
||||||
|
|
||||||
|
/// Handle context for .deb building: locally, over ssh, in a chroot...
|
||||||
|
pub mod context;
|
||||||
|
|
||||||
|
/// Optional callback function (taking 4 arguments)
|
||||||
|
/// - Name of the current main operation (e.g. pulling package)
|
||||||
|
/// - Name of the current nested operation (e.g. cloning git repo)
|
||||||
|
/// - Progress, position, index of current operation (e.g. amount of data downloaded)
|
||||||
|
/// - Total amount for current operation (e.g. size of the file to download)
|
||||||
pub type ProgressCallback<'a> = Option<&'a dyn Fn(&str, &str, usize, usize)>;
|
pub type ProgressCallback<'a> = Option<&'a dyn Fn(&str, &str, usize, usize)>;
|
||||||
|
|
||||||
/// Returns the architecture of current CPU, debian-compatible
|
/// Returns the architecture of current CPU, debian-compatible
|
||||||
|
|||||||
@@ -103,9 +103,9 @@ fn main() {
|
|||||||
.map(|s| s.as_str())
|
.map(|s| s.as_str())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
|
|
||||||
// Since pull is async, we need to block on it
|
|
||||||
let (pb, progress_callback) = ui::create_progress_bar(&multi);
|
let (pb, progress_callback) = ui::create_progress_bar(&multi);
|
||||||
|
|
||||||
|
// Since pull is async, we need to block on it
|
||||||
if let Err(e) = rt.block_on(pull(
|
if let Err(e) = rt.block_on(pull(
|
||||||
package,
|
package,
|
||||||
version,
|
version,
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ fn get_series_from_file(path: &str) -> Result<Vec<String>, Box<dyn Error>> {
|
|||||||
parse_series_csv(&content)
|
parse_series_csv(&content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain a list of series from a distribution
|
||||||
pub async fn get_dist_series(dist: &str) -> Result<Vec<String>, Box<dyn Error>> {
|
pub async fn get_dist_series(dist: &str) -> Result<Vec<String>, Box<dyn Error>> {
|
||||||
if Path::new(format!("/usr/share/distro-info/{dist}.csv").as_str()).exists() {
|
if Path::new(format!("/usr/share/distro-info/{dist}.csv").as_str()).exists() {
|
||||||
get_series_from_file(format!("/usr/share/distro-info/{dist}.csv").as_str())
|
get_series_from_file(format!("/usr/share/distro-info/{dist}.csv").as_str())
|
||||||
@@ -105,6 +106,7 @@ pub async fn get_dist_series(dist: &str) -> Result<Vec<String>, Box<dyn Error>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain the distribution (eg. debian, ubuntu) from a distribution series (eg. noble, bookworm)
|
||||||
pub async fn get_dist_from_series(series: &str) -> Result<String, Box<dyn Error>> {
|
pub async fn get_dist_from_series(series: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let debian_series = get_dist_series("debian").await?;
|
let debian_series = get_dist_series("debian").await?;
|
||||||
if debian_series.contains(&series.to_string()) {
|
if debian_series.contains(&series.to_string()) {
|
||||||
@@ -117,34 +119,55 @@ pub async fn get_dist_from_series(series: &str) -> Result<String, Box<dyn Error>
|
|||||||
Err(format!("Unknown series: {}", series).into())
|
Err(format!("Unknown series: {}", series).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A File used in a source package
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FileEntry {
|
pub struct FileEntry {
|
||||||
|
/// Name of the file
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Size of the file
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
|
/// SHA256 hash for the file
|
||||||
pub sha256: String,
|
pub sha256: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A package 'stanza' as found is 'Sources.gz' files, containing basic information about a source package
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PackageStanza {
|
pub struct PackageStanza {
|
||||||
|
/// Name of the package
|
||||||
pub package: String,
|
pub package: String,
|
||||||
|
/// Version number for the package
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
/// Directory field in the stanza
|
||||||
pub directory: String,
|
pub directory: String,
|
||||||
|
/// Source package format (e.g. '3.0 (quilt)')
|
||||||
pub format: String,
|
pub format: String,
|
||||||
|
/// Vcs-Git field in the stanza
|
||||||
pub vcs_git: Option<String>,
|
pub vcs_git: Option<String>,
|
||||||
|
/// Vcs-Browser field in the stanza
|
||||||
pub vcs_browser: Option<String>,
|
pub vcs_browser: Option<String>,
|
||||||
|
/// Files present in the source package
|
||||||
pub files: Vec<FileEntry>,
|
pub files: Vec<FileEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Source package information
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PackageInfo {
|
pub struct PackageInfo {
|
||||||
pub dist: String,
|
/// Source 'stanza' for the package, containing basic information
|
||||||
pub series: String,
|
|
||||||
pub stanza: PackageStanza,
|
pub stanza: PackageStanza,
|
||||||
|
/// Distribution for the package
|
||||||
|
pub dist: String,
|
||||||
|
/// Distribution series for the package
|
||||||
|
pub series: String,
|
||||||
|
/// Preferred VCS for the source package
|
||||||
|
///
|
||||||
|
/// Should be Launchpad on Ubuntu, and Salsa on Debian
|
||||||
pub preferred_vcs: Option<String>,
|
pub preferred_vcs: Option<String>,
|
||||||
|
/// URL for the files of the source package
|
||||||
pub archive_url: String,
|
pub archive_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageInfo {
|
impl PackageInfo {
|
||||||
|
/// Returns true if the package is a Debian native package (no orig)
|
||||||
pub fn is_native(&self) -> bool {
|
pub fn is_native(&self) -> bool {
|
||||||
self.stanza.format.contains("(native)")
|
self.stanza.format.contains("(native)")
|
||||||
}
|
}
|
||||||
@@ -175,9 +198,7 @@ fn get_base_url(dist: &str) -> &str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Obtain the URL for the 'Release' file of a distribution series
|
||||||
* Obtain the URL for the 'Release' file of a distribution series
|
|
||||||
*/
|
|
||||||
fn get_release_url(base_url: &str, series: &str, pocket: &str) -> String {
|
fn get_release_url(base_url: &str, series: &str, pocket: &str) -> String {
|
||||||
let pocket_full = if pocket.is_empty() {
|
let pocket_full = if pocket.is_empty() {
|
||||||
String::new()
|
String::new()
|
||||||
@@ -187,9 +208,7 @@ fn get_release_url(base_url: &str, series: &str, pocket: &str) -> String {
|
|||||||
format!("{base_url}/dists/{series}{pocket_full}/Release")
|
format!("{base_url}/dists/{series}{pocket_full}/Release")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Obtain the components of a distribution series by parsing the 'Release' file
|
||||||
* Obtain the components of a distribution series by parsing the 'Release' file
|
|
||||||
*/
|
|
||||||
async fn get_components(
|
async fn get_components(
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
series: &str,
|
series: &str,
|
||||||
@@ -296,10 +315,8 @@ impl Iterator for DebianSources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Parse a 'Sources.gz' debian package file data, to look for a target package and
|
||||||
* Parse a 'Sources.gz' debian package file data, to look for a target package and
|
/// return the data for that package stanza
|
||||||
* return the data for that package stanza
|
|
||||||
*/
|
|
||||||
fn parse_sources(
|
fn parse_sources(
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
target_package: &str,
|
target_package: &str,
|
||||||
@@ -314,6 +331,7 @@ fn parse_sources(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get package information from a package, distribution series, and pocket
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
package_name: &str,
|
package_name: &str,
|
||||||
series: &str,
|
series: &str,
|
||||||
@@ -387,6 +405,7 @@ pub async fn get(
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to find package information in a distribution, trying all series and pockets
|
||||||
pub async fn find_package(
|
pub async fn find_package(
|
||||||
package_name: &str,
|
package_name: &str,
|
||||||
dist: &str,
|
dist: &str,
|
||||||
|
|||||||
@@ -333,6 +333,11 @@ async fn fetch_archive_sources(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pull a source package locally
|
||||||
|
///
|
||||||
|
/// Will try to find the package information, and use it to download it over prefered way
|
||||||
|
/// (either git or direct archive download), as well as orig tarball, inside 'package' directory
|
||||||
|
/// The source will be extracted under 'package/package'
|
||||||
pub async fn pull(
|
pub async fn pull(
|
||||||
package: &str,
|
package: &str,
|
||||||
_version: &str,
|
_version: &str,
|
||||||
|
|||||||
Reference in New Issue
Block a user