//! ref: composer/src/Composer/Command/ArchiveCommand.php
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
use shirabe_external_packages::symfony::console::command::command::Command;
use shirabe_external_packages::symfony::console::input::InputInterface;
use shirabe_external_packages::symfony::console::output::OutputInterface;
use shirabe_php_shim::{LogicException, PhpMixed, get_debug_type};
use std::cell::RefCell;
use std::rc::Rc;
use crate::advisory::AuditConfig;
use crate::command::base_command::base_command_initialize;
use crate::command::{BaseCommand, BaseCommandData};
use crate::composer::PartialComposerHandle;
use crate::config::Config;
use crate::console::input::InputArgument;
use crate::console::input::InputOption;
use crate::factory::Factory;
use crate::filter::platform_requirement_filter::PlatformRequirementFilterInterface;
use crate::io::IOInterface;
use crate::io::IOInterfaceImmutable;
use crate::package::archiver::ArchiveManager;
use crate::package::version::VersionParser;
use crate::package::version::VersionSelector;
use crate::plugin::CommandEvent;
use crate::plugin::PluginEvents;
use crate::repository::CompositeRepository;
use crate::repository::RepositoryFactory;
use crate::repository::RepositorySet;
use crate::script::ScriptEvents;
use crate::util::Filesystem;
use crate::util::Platform;
use crate::util::ProcessExecutor;
use crate::util::r#loop::Loop;
#[derive(Debug)]
pub struct ArchiveCommand {
base_command_data: BaseCommandData,
}
impl Default for ArchiveCommand {
fn default() -> Self {
Self::new()
}
}
impl ArchiveCommand {
const FORMATS: &'static [&'static str] = &["tar", "tar.gz", "tar.bz2", "zip"];
pub fn new() -> Self {
let command = ArchiveCommand {
base_command_data: BaseCommandData::new(None),
};
command
.configure()
.expect("ArchiveCommand::configure uses static, valid metadata");
command
}
}
impl Command for ArchiveCommand {
fn configure(&self) -> anyhow::Result<()> {
// TODO(cli-completion): suggest_available_package(99) for `package` argument
self.set_name("archive")?;
self.set_description("Creates an archive of this composer package");
self.set_definition(&[
InputArgument::new("package", Some(InputArgument::OPTIONAL), "The package to archive instead of the current project", None).unwrap().into(),
InputArgument::new("version", Some(InputArgument::OPTIONAL), "A version constraint to find the package to archive", None).unwrap().into(),
InputOption::new("format", Some(shirabe_php_shim::PhpMixed::String("f".to_string())), Some(InputOption::VALUE_REQUIRED), "Format of the resulting archive: tar, tar.gz, tar.bz2 or zip (default tar)", None).unwrap().into(),
InputOption::new("dir", None, Some(InputOption::VALUE_REQUIRED), "Write the archive to this directory", None).unwrap().into(),
InputOption::new("file", None, Some(InputOption::VALUE_REQUIRED), "Write the archive with the given file name. Note that the format will be appended.", None).unwrap().into(),
InputOption::new("ignore-filters", None, Some(InputOption::VALUE_NONE), "Ignore filters when saving package", None).unwrap().into(),
]);
self.set_help(
"The archive command creates an archive of the specified format\n\
containing the files and directories of the Composer project or the specified\n\
package in the specified version and writes it to the specified directory.\n\n\
shirabe archive [--format=zip] [--dir=/foo] [--file=filename] [package [version]]\n\n\
Read more at https://getcomposer.org/doc/03-cli.md#archive"
);
Ok(())
}
fn execute(
&self,
input: Rc>,
output: Rc>,
) -> anyhow::Result {
let composer = self.try_composer(None, None);
let config = if let Some(ref composer) = composer {
let config = composer.borrow_partial().get_config();
// TODO(plugin): dispatch CommandEvent
let command_event =
CommandEvent::new(PluginEvents::COMMAND, "archive", input.clone(), output);
let event_dispatcher = composer.borrow_partial().get_event_dispatcher();
event_dispatcher
.borrow_mut()
.dispatch(Some(command_event.get_name()), None);
event_dispatcher.borrow_mut().dispatch_script(
ScriptEvents::PRE_ARCHIVE_CMD,
true,
vec![],
indexmap::IndexMap::new(),
);
config
} else {
std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?))
};
let format = input
.borrow()
.get_option("format")?
.as_string()
.map(|s| s.to_string())
.unwrap_or_else(|| {
config
.borrow_mut()
.get("archive-format")
.as_string()
.unwrap_or("tar")
.to_string()
});
let dir = input
.borrow()
.get_option("dir")?
.as_string()
.map(|s| s.to_string())
.unwrap_or_else(|| {
config
.borrow_mut()
.get("archive-dir")
.as_string()
.unwrap_or(".")
.to_string()
});
let io = self.get_io().clone();
let return_code = self.archive(
io.clone(),
&config,
input
.borrow()
.get_argument("package")?
.as_string()
.map(|s| s.to_string()),
input
.borrow()
.get_argument("version")?
.as_string()
.map(|s| s.to_string()),
&format,
&dir,
input
.borrow()
.get_option("file")?
.as_string()
.map(|s| s.to_string()),
input
.borrow()
.get_option("ignore-filters")?
.as_bool()
.unwrap_or(false),
composer.as_ref(),
)?;
if return_code == 0
&& let Some(ref composer) = composer
{
composer
.borrow_partial()
.get_event_dispatcher()
.borrow_mut()
.dispatch_script(
ScriptEvents::POST_ARCHIVE_CMD,
true,
vec![],
indexmap::IndexMap::new(),
);
}
Ok(return_code)
}
fn initialize(
&self,
input: Rc>,
output: Rc>,
) -> anyhow::Result<()> {
base_command_initialize(self, input, output)
}
shirabe_external_packages::delegate_command_trait_impls_to_inner!(base_command_data);
}
impl BaseCommand for ArchiveCommand {
fn command_data(
&self,
) -> &shirabe_external_packages::symfony::console::command::command::CommandData {
self.base_command_data.command_data()
}
crate::delegate_base_command_trait_impls_to_inner!(base_command_data);
}
impl ArchiveCommand {
#[allow(clippy::too_many_arguments, reason = "to keep PHP signature")]
pub fn archive(
&self,
io: std::rc::Rc>,
config: &std::rc::Rc>,
package_name: Option,
version: Option,
format: &str,
dest: &str,
file_name: Option,
ignore_filters: bool,
composer: Option<&PartialComposerHandle>,
) -> Result {
let composer_guard = composer.map(crate::command::composer_full);
let mut owned_archive_manager;
let composer_archive_manager;
let mut composer_archive_manager_ref;
let archive_manager: &mut ArchiveManager = if let Some(composer) = &composer_guard {
composer_archive_manager = composer.get_archive_manager().clone();
composer_archive_manager_ref = composer_archive_manager.borrow_mut();
&mut composer_archive_manager_ref
} else {
let factory = Factory;
let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(None)));
let http_downloader = std::rc::Rc::new(std::cell::RefCell::new(
Factory::create_http_downloader(io.clone(), config, indexmap::IndexMap::new())?,
));
let download_manager = factory.create_download_manager(
io.clone(),
config,
&http_downloader,
&process,
None,
)?;
let loop_ = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
http_downloader.clone(),
Some(process),
)));
owned_archive_manager =
factory.create_archive_manager(&config.borrow(), &download_manager, &loop_)?;
&mut owned_archive_manager
};
let package: crate::package::CompletePackageInterfaceHandle =
if let Some(name) = package_name {
match self.select_package(io.clone(), &name, version.as_deref())? {
Some(p) => p,
None => return Ok(1),
}
} else {
let rc = self.require_composer(None, None)?;
let composer = crate::command::composer_full(&rc);
composer.get_package().clone().into()
};
io.write_error(&format!(
"Creating the archive into \"{}\".",
dest
));
let package_path: String = archive_manager.archive(
package,
format.to_string(),
dest.to_string(),
file_name,
ignore_filters,
)?;
let fs = Filesystem::new(None);
let short_path =
fs.find_shortest_path(&Platform::get_cwd(false)?, &package_path, true, false);
io.write_error_no_newline("Created: ");
let display = if short_path.len() < package_path.len() {
&short_path
} else {
&package_path
};
io.write(display);
Ok(0)
}
pub fn select_package(
&self,
io: std::rc::Rc>,
package_name: &str,
version: Option<&str>,
) -> Result