mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
Add package publishing functionality and default publisher management
Signed-off-by: Till Wegmueller <toasterson@gmail.com>
This commit is contained in:
parent
8a32bf3176
commit
a9584fa6d2
4 changed files with 189 additions and 4 deletions
|
|
@ -64,6 +64,37 @@ impl Transaction {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the manifest in the transaction
|
||||||
|
///
|
||||||
|
/// This intelligently merges the provided manifest with the existing one,
|
||||||
|
/// preserving file actions that have already been added to the transaction.
|
||||||
|
///
|
||||||
|
/// The merge strategy:
|
||||||
|
/// - Keeps all file actions from the transaction's manifest (these have been processed with checksums, etc.)
|
||||||
|
/// - Adds any file actions from the provided manifest that don't exist in the transaction's manifest
|
||||||
|
/// - Merges other types of actions (attributes, directories, dependencies, licenses, links) from both manifests
|
||||||
|
pub fn update_manifest(&mut self, manifest: Manifest) {
|
||||||
|
// Keep track of file paths that are already in the transaction's manifest
|
||||||
|
let existing_file_paths: std::collections::HashSet<String> = self.manifest.files
|
||||||
|
.iter()
|
||||||
|
.map(|f| f.path.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Add file actions from the provided manifest that don't exist in the transaction's manifest
|
||||||
|
for file in manifest.files {
|
||||||
|
if !existing_file_paths.contains(&file.path) {
|
||||||
|
self.manifest.add_file(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge other types of actions
|
||||||
|
self.manifest.attributes.extend(manifest.attributes);
|
||||||
|
self.manifest.directories.extend(manifest.directories);
|
||||||
|
self.manifest.dependencies.extend(manifest.dependencies);
|
||||||
|
self.manifest.licenses.extend(manifest.licenses);
|
||||||
|
self.manifest.links.extend(manifest.links);
|
||||||
|
}
|
||||||
|
|
||||||
/// Process a file for the transaction
|
/// Process a file for the transaction
|
||||||
///
|
///
|
||||||
/// Takes a FileAction and a path to a file in a prototype directory.
|
/// Takes a FileAction and a path to a file in a prototype directory.
|
||||||
|
|
@ -299,6 +330,11 @@ impl Repository for FileBackend {
|
||||||
fs::create_dir_all(self.path.join("catalog").join(publisher))?;
|
fs::create_dir_all(self.path.join("catalog").join(publisher))?;
|
||||||
fs::create_dir_all(self.path.join("pkg").join(publisher))?;
|
fs::create_dir_all(self.path.join("pkg").join(publisher))?;
|
||||||
|
|
||||||
|
// Set as default publisher if no default publisher is set
|
||||||
|
if self.config.default_publisher.is_none() {
|
||||||
|
self.config.default_publisher = Some(publisher.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// Save the updated configuration
|
// Save the updated configuration
|
||||||
self.save_config()?;
|
self.save_config()?;
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +453,7 @@ impl Repository for FileBackend {
|
||||||
// For each package, list contents
|
// For each package, list contents
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
|
|
||||||
for (pkg_name, pkg_version, pub_name) in packages {
|
for (pkg_name, pkg_version, _pub_name) in packages {
|
||||||
// Example content data (package, path, type)
|
// Example content data (package, path, type)
|
||||||
let example_contents = vec![
|
let example_contents = vec![
|
||||||
(format!("{}@{}", pkg_name, pkg_version), "/usr/bin/example".to_string(), "file".to_string()),
|
(format!("{}@{}", pkg_name, pkg_version), "/usr/bin/example".to_string(), "file".to_string()),
|
||||||
|
|
@ -504,6 +540,22 @@ impl Repository for FileBackend {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the default publisher for the repository
|
||||||
|
fn set_default_publisher(&mut self, publisher: &str) -> Result<()> {
|
||||||
|
// Check if the publisher exists
|
||||||
|
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||||
|
return Err(anyhow!("Publisher does not exist: {}", publisher));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default publisher
|
||||||
|
self.config.default_publisher = Some(publisher.to_string());
|
||||||
|
|
||||||
|
// Save the updated configuration
|
||||||
|
self.save_config()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileBackend {
|
impl FileBackend {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ pub struct RepositoryConfig {
|
||||||
pub version: RepositoryVersion,
|
pub version: RepositoryVersion,
|
||||||
pub publishers: Vec<String>,
|
pub publishers: Vec<String>,
|
||||||
pub properties: HashMap<String, String>,
|
pub properties: HashMap<String, String>,
|
||||||
|
pub default_publisher: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RepositoryConfig {
|
impl Default for RepositoryConfig {
|
||||||
|
|
@ -53,6 +54,7 @@ impl Default for RepositoryConfig {
|
||||||
version: RepositoryVersion::default(),
|
version: RepositoryVersion::default(),
|
||||||
publishers: Vec::new(),
|
publishers: Vec::new(),
|
||||||
properties: HashMap::new(),
|
properties: HashMap::new(),
|
||||||
|
default_publisher: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,4 +96,7 @@ pub trait Repository {
|
||||||
|
|
||||||
/// Refresh repository metadata
|
/// Refresh repository metadata
|
||||||
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
||||||
|
|
||||||
|
/// Set the default publisher for the repository
|
||||||
|
fn set_default_publisher(&mut self, publisher: &str) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
@ -3,9 +3,8 @@
|
||||||
// MPL was not distributed with this file, You can
|
// MPL was not distributed with this file, You can
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{Repository, RepositoryConfig, RepositoryVersion};
|
use super::{Repository, RepositoryConfig, RepositoryVersion};
|
||||||
|
|
||||||
|
|
@ -299,6 +298,25 @@ impl Repository for RestBackend {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the default publisher for the repository
|
||||||
|
fn set_default_publisher(&mut self, publisher: &str) -> Result<()> {
|
||||||
|
// This is a stub implementation
|
||||||
|
// In a real implementation, we would make a REST API call to set the default publisher
|
||||||
|
|
||||||
|
// Check if the publisher exists
|
||||||
|
if !self.config.publishers.contains(&publisher.to_string()) {
|
||||||
|
return Err(anyhow!("Publisher does not exist: {}", publisher));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default publisher
|
||||||
|
self.config.default_publisher = Some(publisher.to_string());
|
||||||
|
|
||||||
|
// Save the updated configuration
|
||||||
|
self.save_config()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestBackend {
|
impl RestBackend {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use libips::actions::{ActionError, File, Manifest};
|
use libips::actions::{ActionError, File, Manifest};
|
||||||
|
use libips::repository::{Repository, FileBackend};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Result, anyhow};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{read_dir, OpenOptions};
|
use std::fs::{read_dir, OpenOptions};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
@ -31,6 +32,24 @@ enum Commands {
|
||||||
ShowComponent {
|
ShowComponent {
|
||||||
component: String,
|
component: String,
|
||||||
},
|
},
|
||||||
|
/// Publish a package to a repository
|
||||||
|
Publish {
|
||||||
|
/// Path to the manifest file
|
||||||
|
#[clap(short = 'm', long)]
|
||||||
|
manifest_path: PathBuf,
|
||||||
|
|
||||||
|
/// Path to the prototype directory containing the files to publish
|
||||||
|
#[clap(short = 'p', long)]
|
||||||
|
prototype_dir: PathBuf,
|
||||||
|
|
||||||
|
/// Path to the repository
|
||||||
|
#[clap(short = 'r', long)]
|
||||||
|
repo_path: PathBuf,
|
||||||
|
|
||||||
|
/// Publisher name (defaults to "test" if not specified)
|
||||||
|
#[clap(short = 'u', long)]
|
||||||
|
publisher: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
|
@ -43,6 +62,12 @@ fn main() -> Result<()> {
|
||||||
replacements,
|
replacements,
|
||||||
output_manifest,
|
output_manifest,
|
||||||
} => diff_component(component, replacements, output_manifest),
|
} => diff_component(component, replacements, output_manifest),
|
||||||
|
Commands::Publish {
|
||||||
|
manifest_path,
|
||||||
|
prototype_dir,
|
||||||
|
repo_path,
|
||||||
|
publisher,
|
||||||
|
} => publish_package(manifest_path, prototype_dir, repo_path, publisher),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,3 +310,88 @@ fn make_file_map(files: Vec<File>) -> HashMap<String, File> {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Publish a package to a repository
|
||||||
|
///
|
||||||
|
/// This function:
|
||||||
|
/// 1. Opens the repository at the specified path
|
||||||
|
/// 2. Parses the manifest file
|
||||||
|
/// 3. Uses the FileBackend's publish_files method to publish the files from the prototype directory
|
||||||
|
fn publish_package(
|
||||||
|
manifest_path: &PathBuf,
|
||||||
|
prototype_dir: &PathBuf,
|
||||||
|
repo_path: &PathBuf,
|
||||||
|
publisher: &Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Check if the manifest file exists
|
||||||
|
if !manifest_path.exists() {
|
||||||
|
return Err(anyhow!("Manifest file does not exist: {}", manifest_path.display()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the prototype directory exists
|
||||||
|
if !prototype_dir.exists() {
|
||||||
|
return Err(anyhow!("Prototype directory does not exist: {}", prototype_dir.display()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the manifest file
|
||||||
|
println!("Parsing manifest file: {}", manifest_path.display());
|
||||||
|
let manifest = Manifest::parse_file(manifest_path)?;
|
||||||
|
|
||||||
|
// Open the repository
|
||||||
|
println!("Opening repository at: {}", repo_path.display());
|
||||||
|
let repo = match FileBackend::open(repo_path) {
|
||||||
|
Ok(repo) => repo,
|
||||||
|
Err(_) => {
|
||||||
|
println!("Repository does not exist, creating a new one...");
|
||||||
|
// Create a new repository with version 4
|
||||||
|
FileBackend::create(repo_path, libips::repository::RepositoryVersion::V4)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine which publisher to use
|
||||||
|
let publisher_name = if let Some(pub_name) = publisher {
|
||||||
|
// Use the explicitly specified publisher
|
||||||
|
if !repo.config.publishers.contains(pub_name) {
|
||||||
|
return Err(anyhow!("Publisher '{}' does not exist in the repository. Please add it first using pkg6repo add-publisher.", pub_name));
|
||||||
|
}
|
||||||
|
pub_name.clone()
|
||||||
|
} else {
|
||||||
|
// Use the default publisher
|
||||||
|
match &repo.config.default_publisher {
|
||||||
|
Some(default_pub) => default_pub.clone(),
|
||||||
|
None => return Err(anyhow!("No default publisher set in the repository. Please specify a publisher using the --publisher option or set a default publisher."))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Begin a transaction
|
||||||
|
println!("Beginning transaction for publisher: {}", publisher_name);
|
||||||
|
let mut transaction = repo.begin_transaction()?;
|
||||||
|
|
||||||
|
// Add files from the prototype directory to the transaction
|
||||||
|
println!("Adding files from prototype directory: {}", prototype_dir.display());
|
||||||
|
for file_action in manifest.files.iter() {
|
||||||
|
// Construct the full path to the file in the prototype directory
|
||||||
|
let file_path = prototype_dir.join(&file_action.path);
|
||||||
|
|
||||||
|
// Check if the file exists
|
||||||
|
if !file_path.exists() {
|
||||||
|
println!("Warning: File does not exist in prototype directory: {}", file_path.display());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the file to the transaction
|
||||||
|
println!("Adding file: {}", file_action.path);
|
||||||
|
transaction.add_file(file_action.clone(), &file_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the manifest in the transaction
|
||||||
|
println!("Updating manifest in the transaction...");
|
||||||
|
transaction.update_manifest(manifest);
|
||||||
|
|
||||||
|
// Commit the transaction
|
||||||
|
println!("Committing transaction...");
|
||||||
|
transaction.commit()?;
|
||||||
|
|
||||||
|
println!("Package published successfully!");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue