feat: publish members when publishing workspace

This commit is contained in:
daimond113 2024-10-12 16:00:21 +02:00
parent 9a64a12f8e
commit fa00a97f8b
No known key found for this signature in database
GPG key ID: 3A8ECE51328B513C
6 changed files with 57 additions and 25 deletions

View file

@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] - 2024-10-07 ## [Unreleased]
### Added
- Add `yes` argument to skip all prompts in publish command by @daimond113
- Publish all workspace members when publishing a workspace by @daimond113
### Fixed ### Fixed
- Add feature gates to `wally-compat` specific code in init command by @daimond113 - Add feature gates to `wally-compat` specific code in init command by @daimond113

View file

@ -131,7 +131,7 @@ impl InstallCommand {
println!( println!(
"\n{}\n", "\n{}\n",
format!("[now installing {}]", manifest.name) format!("[now installing {} {}]", manifest.name, manifest.target)
.bold() .bold()
.on_bright_black() .on_bright_black()
); );

View file

@ -9,7 +9,7 @@ use std::{
}; };
use tempfile::tempfile; use tempfile::tempfile;
use crate::cli::up_to_date_lockfile; use crate::cli::{run_on_workspace_members, up_to_date_lockfile};
use pesde::{ use pesde::{
manifest::{target::Target, DependencyType}, manifest::{target::Target, DependencyType},
scripts::ScriptName, scripts::ScriptName,
@ -23,19 +23,30 @@ use pesde::{
Project, DEFAULT_INDEX_NAME, MANIFEST_FILE_NAME, Project, DEFAULT_INDEX_NAME, MANIFEST_FILE_NAME,
}; };
#[derive(Debug, Args)] #[derive(Debug, Args, Copy, Clone)]
pub struct PublishCommand { pub struct PublishCommand {
/// Whether to output a tarball instead of publishing /// Whether to output a tarball instead of publishing
#[arg(short, long)] #[arg(short, long)]
dry_run: bool, dry_run: bool,
/// Agree to all prompts
#[arg(short, long)]
yes: bool,
} }
impl PublishCommand { impl PublishCommand {
pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> { fn run_impl(self, project: &Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
let mut manifest = project let mut manifest = project
.deser_manifest() .deser_manifest()
.context("failed to read manifest")?; .context("failed to read manifest")?;
println!(
"\n{}\n",
format!("[now publishing {} {}]", manifest.name, manifest.target)
.bold()
.on_bright_black()
);
if manifest.private { if manifest.private {
println!("{}", "package is private, cannot publish".red().bold()); println!("{}", "package is private, cannot publish".red().bold());
@ -54,7 +65,7 @@ impl PublishCommand {
anyhow::bail!("no build files found in target"); anyhow::bail!("no build files found in target");
} }
match up_to_date_lockfile(&project)? { match up_to_date_lockfile(project)? {
Some(lockfile) => { Some(lockfile) => {
if lockfile if lockfile
.graph .graph
@ -313,7 +324,7 @@ impl PublishCommand {
} }
DependencySpecifiers::Workspace(spec) => { DependencySpecifiers::Workspace(spec) => {
let pkg_ref = WorkspacePackageSource let pkg_ref = WorkspacePackageSource
.resolve(spec, &project, target_kind) .resolve(spec, project, target_kind)
.context("failed to resolve workspace package")? .context("failed to resolve workspace package")?
.1 .1
.pop_last() .pop_last()
@ -410,7 +421,10 @@ impl PublishCommand {
display_includes.into_iter().collect::<Vec<_>>().join(", ") display_includes.into_iter().collect::<Vec<_>>().join(", ")
); );
if !self.dry_run && !inquire::Confirm::new("is this information correct?").prompt()? { if !self.dry_run
&& !self.yes
&& !inquire::Confirm::new("is this information correct?").prompt()?
{
println!("\n{}", "publish aborted".red().bold()); println!("\n{}", "publish aborted".red().bold());
return Ok(()); return Ok(());
@ -447,10 +461,10 @@ impl PublishCommand {
.clone(), .clone(),
); );
source source
.refresh(&project) .refresh(project)
.context("failed to refresh source")?; .context("failed to refresh source")?;
let config = source let config = source
.config(&project) .config(project)
.context("failed to get source config")?; .context("failed to get source config")?;
if archive.len() > config.max_archive_size { if archive.len() > config.max_archive_size {
@ -497,30 +511,36 @@ impl PublishCommand {
match status { match status {
StatusCode::CONFLICT => { StatusCode::CONFLICT => {
println!("{}", "package version already exists".red().bold()); println!("{}", "package version already exists".red().bold());
Ok(())
} }
StatusCode::FORBIDDEN => { StatusCode::FORBIDDEN => {
println!( println!(
"{}", "{}",
"unauthorized to publish under this scope".red().bold() "unauthorized to publish under this scope".red().bold()
); );
Ok(())
} }
StatusCode::BAD_REQUEST => { StatusCode::BAD_REQUEST => {
println!("{}: {text}", "invalid package".red().bold()); println!("{}: {text}", "invalid package".red().bold());
Ok(())
} }
code if !code.is_success() => { code if !code.is_success() => {
anyhow::bail!("failed to publish package: {code} ({text})"); anyhow::bail!("failed to publish package: {code} ({text})");
} }
_ => { _ => {
println!("{text}"); println!("{text}");
}
}
Ok(()) Ok(())
} }
pub fn run(self, project: Project, reqwest: reqwest::blocking::Client) -> anyhow::Result<()> {
let result = self.run_impl(&project, reqwest.clone());
if project.workspace_dir().is_some() {
return result;
} else if let Err(result) = result {
println!("an error occurred publishing workspace root: {result}");
} }
run_on_workspace_members(&project, |project| self.run_impl(&project, reqwest.clone()))
.map(|_| ())
} }
} }

View file

@ -1,6 +1,7 @@
use crate::cli::{download_graph, run_on_workspace_members}; use crate::cli::{download_graph, run_on_workspace_members};
use anyhow::Context; use anyhow::Context;
use clap::Args; use clap::Args;
use colored::Colorize;
use indicatif::MultiProgress; use indicatif::MultiProgress;
use pesde::{lockfile::Lockfile, Project}; use pesde::{lockfile::Lockfile, Project};
use std::collections::HashSet; use std::collections::HashSet;
@ -25,6 +26,13 @@ impl UpdateCommand {
.deser_manifest() .deser_manifest()
.context("failed to read manifest")?; .context("failed to read manifest")?;
println!(
"\n{}\n",
format!("[now updating {} {}]", manifest.name, manifest.target)
.bold()
.on_bright_black()
);
let graph = project let graph = project
.dependency_graph(None, &mut refreshed_sources) .dependency_graph(None, &mut refreshed_sources)
.context("failed to build dependency graph")?; .context("failed to build dependency graph")?;

View file

@ -272,8 +272,8 @@ pub fn run_on_workspace_members(
) -> anyhow::Result<BTreeMap<PackageName, BTreeMap<TargetKind, RelativePathBuf>>> { ) -> anyhow::Result<BTreeMap<PackageName, BTreeMap<TargetKind, RelativePathBuf>>> {
Ok(match project.workspace_dir() { Ok(match project.workspace_dir() {
Some(_) => { Some(_) => {
// this might seem counterintuitive, but remember that the workspace // this might seem counterintuitive, but remember that
// is the package_dir when the user isn't in a member package // the presence of a workspace dir means that this project is a member of one
Default::default() Default::default()
} }
None => project None => project

View file

@ -130,27 +130,27 @@ impl Project {
} }
} }
/// Access the package directory /// The directory of the package
pub fn package_dir(&self) -> &Path { pub fn package_dir(&self) -> &Path {
&self.package_dir &self.package_dir
} }
/// Access the workspace directory /// The directory of the workspace this package belongs to, if any
pub fn workspace_dir(&self) -> Option<&Path> { pub fn workspace_dir(&self) -> Option<&Path> {
self.workspace_dir.as_deref() self.workspace_dir.as_deref()
} }
/// Access the data directory /// The directory to store general-purpose data
pub fn data_dir(&self) -> &Path { pub fn data_dir(&self) -> &Path {
&self.data_dir &self.data_dir
} }
/// Access the authentication configuration /// The authentication configuration
pub fn auth_config(&self) -> &AuthConfig { pub fn auth_config(&self) -> &AuthConfig {
&self.auth_config &self.auth_config
} }
/// Access the CAS (content-addressable storage) directory /// The CAS (content-addressable storage) directory
pub fn cas_dir(&self) -> &Path { pub fn cas_dir(&self) -> &Path {
&self.cas_dir &self.cas_dir
} }