From c80e6daf0fc28aa34b5303da80e032238550c1c2 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Tue, 28 Jan 2025 12:55:43 -0500 Subject: [PATCH 01/26] Correct name of dist.artifacts.checksum in v1 config. --- cargo-dist/src/init/apply_dist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cargo-dist/src/init/apply_dist.rs b/cargo-dist/src/init/apply_dist.rs index 908fb70d0..e9ec7adb0 100644 --- a/cargo-dist/src/init/apply_dist.rs +++ b/cargo-dist/src/init/apply_dist.rs @@ -361,7 +361,7 @@ fn apply_artifacts(table: &mut toml_edit::Table, artifacts: &Option Date: Tue, 28 Jan 2025 12:56:28 -0500 Subject: [PATCH 02/26] Implement apply_ci_common() --- cargo-dist/src/init/apply_dist.rs | 175 ++++++++++++++++++------------ 1 file changed, 103 insertions(+), 72 deletions(-) diff --git a/cargo-dist/src/init/apply_dist.rs b/cargo-dist/src/init/apply_dist.rs index e9ec7adb0..ca63a31d3 100644 --- a/cargo-dist/src/init/apply_dist.rs +++ b/cargo-dist/src/init/apply_dist.rs @@ -110,56 +110,6 @@ pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) // TODO(migration): make sure all of these are handled /* - - apply_optional_value( - table, - "checksum", - "# Checksums to generate for each App\n", - checksum.map(|c| c.ext().as_str()), - ); - - apply_optional_value( - table, - "merge-tasks", - "# Whether to run otherwise-parallelizable tasks on the same machine\n", - *merge_tasks, - ); - - apply_optional_value( - table, - "fail-fast", - "# Whether failing tasks should make us give up on all other tasks\n", - *fail_fast, - ); - - apply_optional_value( - table, - "cache-builds", - "# Whether builds should try to be cached in CI\n", - *cache_builds, - ); - - apply_optional_value( - table, - "build-local-artifacts", - "# Whether CI should include auto-generated code to build local artifacts\n", - *build_local_artifacts, - ); - - apply_optional_value( - table, - "dispatch-releases", - "# Whether CI should trigger releases with dispatches instead of tag pushes\n", - *dispatch_releases, - ); - - apply_optional_value( - table, - "release-branch", - "# Trigger releases on pushes to this branch instead of tag pushes\n", - release_branch.as_ref(), - ); - apply_optional_value( table, "create-release", @@ -190,13 +140,6 @@ pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) .map(|a| a.to_string()), ); - apply_string_list( - table, - "plan-jobs", - "# Plan jobs to run in CI\n", - plan_jobs.as_ref(), - ); - apply_string_list( table, "local-artifacts-jobs", @@ -246,13 +189,6 @@ pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) *force_latest, ); - apply_optional_value( - table, - "pr-run-mode", - "# Which actions to run on pull requests\n", - pr_run_mode.as_ref().map(|m| m.to_string()), - ); - apply_optional_value( table, "github-attestations", @@ -267,13 +203,6 @@ pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) hosting.as_ref(), ); - apply_optional_value( - table, - "tag-namespace", - "# A prefix git tags must include for dist to care about them\n", - tag_namespace.as_ref(), - ); - apply_optional_value( table, "install-updater", @@ -580,7 +509,8 @@ fn apply_ci(table: &mut toml_edit::Table, ci: &Option) { panic!("Expected [dist.ci] to be a table"); }; - // TODO(migration): implement this + + apply_ci_common(ci_table, &ci.common); // Finalize the table ci_table @@ -588,6 +518,107 @@ fn apply_ci(table: &mut toml_edit::Table, ci: &Option) { .set_prefix("\n# CI configuration for dist\n"); } + +fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { + apply_optional_value( + table, + "merge-tasks", + "# Whether to run otherwise-parallelizable tasks on the same machine\n", + common.merge_tasks, + ); + + apply_optional_value( + table, + "fail-fast", + "# Whether failing tasks should make us give up on all other tasks\n", + common.fail_fast, + ); + + apply_optional_value( + table, + "cache-builds", + "# Whether builds should try to be cached in CI\n", + common.cache_builds, + ); + + apply_optional_value( + table, + "build-local-artifacts", + "# Whether CI should include auto-generated code to build local artifacts\n", + common.build_local_artifacts, + ); + + apply_optional_value( + table, + "dispatch-releases", + "# Whether CI should trigger releases with dispatches instead of tag pushes\n", + common.dispatch_releases, + ); + + apply_optional_value( + table, + "release-branch", + "# Trigger releases on pushes to this branch instead of tag pushes\n", + common.release_branch.as_ref(), + ); + + apply_optional_value( + table, + "pr-run-mode", + "# Which actions to run on pull requests\n", + common.pr_run_mode.as_ref().map(|m| m.to_string()), + ); + + apply_optional_value( + table, + "tag-namespace", + "# A prefix git tags must include for dist to care about them\n", + common.tag_namespace.as_ref(), + ); + + apply_string_list( + table, + "plan-jobs", + "# Additional plan jobs to run in CI\n", + common.plan_jobs.as_ref(), + ); + + apply_string_list( + table, + "build-local-jobs", + "# Additional local artifacts jobs to run in CI\n", + common.build_local_jobs.as_ref(), + ); + + apply_string_list( + table, + "build-global-jobs", + "# Additional global artifacts jobs to run in CI\n", + common.build_global_jobs.as_ref(), + ); + + apply_string_list( + table, + "host-jobs", + "# Additional hosts jobs to run in CI\n", + common.host_jobs.as_ref(), + ); + + apply_string_list( + table, + "publish-jobs", + "# Additional publish jobs to run in CI\n", + common.publish_jobs.as_ref(), + ); + + apply_string_list( + table, + "post-announce-jobs", + "# Additional jobs to run in CI, after the announce job finishes\n", + common.post_announce_jobs.as_ref(), + ); +} + fn apply_hosts(table: &mut toml_edit::Table, hosts: &Option) { let Some(hosts_table) = table.get_mut("hosts") else { // Nothing to do. From c4f1358ece8cccf18c4a3cb1fc2fbb986de4c1d1 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Tue, 28 Jan 2025 14:22:23 -0500 Subject: [PATCH 03/26] Add apply_ci_github(). --- cargo-dist/src/init/apply_dist.rs | 54 ++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/cargo-dist/src/init/apply_dist.rs b/cargo-dist/src/init/apply_dist.rs index ca63a31d3..3368b8ee4 100644 --- a/cargo-dist/src/init/apply_dist.rs +++ b/cargo-dist/src/init/apply_dist.rs @@ -509,15 +509,67 @@ fn apply_ci(table: &mut toml_edit::Table, ci: &Option) { panic!("Expected [dist.ci] to be a table"); }; - apply_ci_common(ci_table, &ci.common); + if let Some(github) = &ci.github { + match github { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "github", + "# Whether to use GitHub CI\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_ci_github(installers_table, v); + } + } + } + // Finalize the table ci_table .decor_mut() .set_prefix("\n# CI configuration for dist\n"); } +fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { + let Some(gh_table) = ci_table.get_mut("github") else { + return; + }; + let toml_edit::Item::Table(gh_table) = gh_table else { + panic!("Expected [dist.ci.github] to be a table"); + }; + + apply_installers_common(gh_table, &github.common); + + apply_optional_value( + gh_table, + "runners", + "# Custom GitHub runners, specified as target triples\n", + github.runners, + ); + + apply_optional_value( + gh_table, + "permissions", + "# Custom permissions for jobs\n", + github.permissions, + ); + + apply_optional_value( + gh_table, + "build-setup", + "# Custom permissions for jobs\n", + github.build_setup, + ); + + // Finalize the table + gh_table + .decor_mut() + .set_prefix("\n# Configure GitHub CI\n"); +} + fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { apply_optional_value( From bd56b719ddaa81ffe9618909423b24cffc978a1f Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Tue, 28 Jan 2025 17:28:39 -0500 Subject: [PATCH 04/26] Split init/apply_dist.rs into init/apply_dist/* --- cargo-dist/src/init/apply_dist.rs | 1079 ----------------- cargo-dist/src/init/apply_dist/artifacts.rs | 100 ++ cargo-dist/src/init/apply_dist/builds.rs | 142 +++ cargo-dist/src/init/apply_dist/ci.rs | 184 +++ cargo-dist/src/init/apply_dist/helpers.rs | 85 ++ cargo-dist/src/init/apply_dist/hosts.rs | 22 + cargo-dist/src/init/apply_dist/installers.rs | 300 +++++ cargo-dist/src/init/apply_dist/mod.rs | 254 ++++ cargo-dist/src/init/apply_dist/publishers.rs | 20 + .../init/apply_dist/system_dependencies.rs | 16 + cargo-dist/src/init/mod.rs | 7 + 11 files changed, 1130 insertions(+), 1079 deletions(-) delete mode 100644 cargo-dist/src/init/apply_dist.rs create mode 100644 cargo-dist/src/init/apply_dist/artifacts.rs create mode 100644 cargo-dist/src/init/apply_dist/builds.rs create mode 100644 cargo-dist/src/init/apply_dist/ci.rs create mode 100644 cargo-dist/src/init/apply_dist/helpers.rs create mode 100644 cargo-dist/src/init/apply_dist/hosts.rs create mode 100644 cargo-dist/src/init/apply_dist/installers.rs create mode 100644 cargo-dist/src/init/apply_dist/mod.rs create mode 100644 cargo-dist/src/init/apply_dist/publishers.rs create mode 100644 cargo-dist/src/init/apply_dist/system_dependencies.rs diff --git a/cargo-dist/src/init/apply_dist.rs b/cargo-dist/src/init/apply_dist.rs deleted file mode 100644 index 3368b8ee4..000000000 --- a/cargo-dist/src/init/apply_dist.rs +++ /dev/null @@ -1,1079 +0,0 @@ -use crate::{platform::MinGlibcVersion, METADATA_DIST}; -use crate::config::{InstallPathStrategy, SystemDependencies}; -use crate::config::v1::{ - artifacts::archives::ArchiveLayer, - artifacts::ArtifactLayer, - builds::BuildLayer, - ci::CiLayer, - hosts::HostLayer, - layer::BoolOr, - publishers::PublisherLayer, - TomlLayer, -}; -use crate::config::v1::installers::{ - homebrew::HomebrewInstallerLayer, msi::MsiInstallerLayer, npm::NpmInstallerLayer, - pkg::PkgInstallerLayer, powershell::PowershellInstallerLayer, - shell::ShellInstallerLayer, CommonInstallerLayer, InstallerLayer, -}; -use axoasset::toml_edit; - -use crate::config::v1::layer::BoolOrOptExt; - -/// Update a workspace toml-edit document with the current DistMetadata value -pub fn apply_dist_to_workspace_toml( - workspace_toml: &mut toml_edit::DocumentMut, - meta: &TomlLayer, -) { - let metadata = workspace_toml.as_item_mut(); - apply_dist_to_metadata(metadata, meta); -} - -/// Ensure [dist] has the given values -pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) { - let dist_metadata = &mut metadata[METADATA_DIST]; - - // If there's no table, make one - if !dist_metadata.is_table() { - *dist_metadata = toml_edit::table(); - } - - // Apply formatted/commented values - let table = dist_metadata.as_table_mut().unwrap(); - - // This is intentionally written awkwardly to make you update this - let TomlLayer { - config_version, - dist_version, - dist_url_override, - dist, - allow_dirty, - targets, - artifacts, - builds, - ci, - hosts, - installers, - publishers, - } = &meta; - - let installers = &Some(apply_default_install_path(installers)); - - apply_optional_value( - table, - "config-version", - "# The configuration version to use (valid options: 1)\n", - Some(config_version.to_string()), - ); - - apply_optional_value( - table, - "dist-version", - "# The preferred dist version to use in CI (Cargo.toml SemVer syntax)\n", - dist_version.as_ref().map(|v| v.to_string()), - ); - - apply_optional_value( - table, - "dist-url-override", - "# A URL to use to install `cargo-dist` (with the installer script)\n", - dist_url_override.as_ref().map(|v| v.to_string()), - ); - - apply_optional_value( - table, - "dist", - "# Whether the package should be distributed/built by dist (defaults to true)\n", - *dist, - ); - - apply_string_list( - table, - "allow-dirty", - "# Skip checking whether the specified configuration files are up to date\n", - allow_dirty.as_ref(), - ); - - apply_string_list( - table, - "targets", - "# Target platforms to build apps for (Rust target-triple syntax)\n", - targets.as_ref(), - ); - - apply_artifacts(table, artifacts); - apply_builds(table, builds); - apply_ci(table, ci); - apply_hosts(table, hosts); - apply_installers(table, installers); - apply_publishers(table, publishers); - - // TODO(migration): make sure all of these are handled - /* - - apply_optional_value( - table, - "create-release", - "# Whether dist should create a Github Release or use an existing draft\n", - *create_release, - ); - - apply_optional_value( - table, - "github-release", - "# Which phase dist should use to create the GitHub release\n", - github_release.as_ref().map(|a| a.to_string()), - ); - - apply_optional_value( - table, - "github-releases-repo", - "# Publish GitHub Releases to this repo instead\n", - github_releases_repo.as_ref().map(|a| a.to_string()), - ); - - apply_optional_value( - table, - "github-releases-submodule-path", - "# Read the commit to be tagged from the submodule at this path\n", - github_releases_submodule_path - .as_ref() - .map(|a| a.to_string()), - ); - - apply_string_list( - table, - "local-artifacts-jobs", - "# Local artifacts jobs to run in CI\n", - local_artifacts_jobs.as_ref(), - ); - - apply_string_list( - table, - "global-artifacts-jobs", - "# Global artifacts jobs to run in CI\n", - global_artifacts_jobs.as_ref(), - ); - - apply_string_list( - table, - "host-jobs", - "# Host jobs to run in CI\n", - host_jobs.as_ref(), - ); - - apply_string_list( - table, - "publish-jobs", - "# Publish jobs to run in CI\n", - publish_jobs.as_ref(), - ); - - apply_string_list( - table, - "post-announce-jobs", - "# Post-announce jobs to run in CI\n", - post_announce_jobs.as_ref(), - ); - - apply_optional_value( - table, - "publish-prereleases", - "# Whether to publish prereleases to package managers\n", - *publish_prereleases, - ); - - apply_optional_value( - table, - "force-latest", - "# Always mark releases as latest, ignoring semver semantics\n", - *force_latest, - ); - - apply_optional_value( - table, - "github-attestations", - "# Whether to enable GitHub Attestations\n", - *github_attestations, - ); - - apply_string_or_list( - table, - "hosting", - "# Where to host releases\n", - hosting.as_ref(), - ); - - apply_optional_value( - table, - "install-updater", - "# Whether to install an updater program\n", - *install_updater, - ); - - apply_optional_value( - table, - "always-use-latest-updater", - "# Whether to always use the latest updater instead of a specific known-good version\n", - *always_use_latest_updater, - ); - - apply_optional_value( - table, - "display", - "# Whether to display this app's installers/artifacts in release bodies\n", - *display, - ); - - apply_optional_value( - table, - "display-name", - "# Custom display name to use for this app in release bodies\n", - display_name.as_ref(), - ); - - - - */ - - // Finalize the table - table.decor_mut().set_prefix("\n# Config for 'dist'\n"); -} - -fn apply_default_install_path(installers: &Option) -> InstallerLayer { - let mut installers = installers.clone().unwrap_or_default(); - - // Forcibly inline the default install_path if not specified, - // and if we've specified a shell or powershell installer - let install_path = if installers.common.install_path.is_none() - && !(installers.shell.is_none_or_false() || installers.powershell.is_none_or_false()) - { - Some(InstallPathStrategy::default_list()) - } else { - installers.common.install_path.clone() - }; - - installers.common.install_path = install_path; - installers -} - -fn apply_artifacts(table: &mut toml_edit::Table, artifacts: &Option) { - let Some(artifacts) = artifacts else { - return; - }; - let Some(artifacts_table) = table.get_mut("artifacts") else { - return; - }; - let toml_edit::Item::Table(artifacts_table) = artifacts_table else { - panic!("Expected [dist.artifacts] to be a table"); - }; - - // TODO(migration): implement this - - apply_artifacts_archives(artifacts_table, &artifacts.archives); - - apply_optional_value( - artifacts_table, - "source-tarball", - "# Generate and dist a source tarball\n", - artifacts.source_tarball, - ); - - // TODO(migration): implement dist.artifacts.extra. - /* - apply_optional_value( - artifacts_table, - "extra", - "# Any extra artifacts, and their build scripts\n", - artifacts.extra, - ); - */ - - apply_optional_value( - artifacts_table, - "checksum", - "# The checksum format to generate\n", - artifacts.checksum.map(|cs| cs.to_string()), - ); - - // Finalize the table - artifacts_table - .decor_mut() - .set_prefix("\n# Artifact configuration for dist\n"); -} - -fn apply_artifacts_archives( - artifacts_table: &mut toml_edit::Table, - archives: &Option, -) { - let Some(archives) = archives else { - return; - }; - let Some(archives_table) = artifacts_table.get_mut("archives") else { - return; - }; - let toml_edit::Item::Table(archives_table) = archives_table else { - panic!("Expected [dist.artifacts.archives] to be a table"); - }; - - apply_string_list( - archives_table, - "include", - "# Extra static files to include in each App (path relative to this Cargo.toml's dir)\n", - archives.include.as_ref(), - ); - - apply_optional_value( - archives_table, - "auto-includes", - "# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)\n", - archives.auto_includes, - ); - - apply_optional_value( - archives_table, - "windows-archive", - "# The archive format to use for windows builds (defaults .zip)\n", - archives.windows_archive.map(|a| a.ext()), - ); - - apply_optional_value( - archives_table, - "unix-archive", - "# The archive format to use for non-windows builds (defaults .tar.xz)\n", - archives.unix_archive.map(|a| a.ext()), - ); - - apply_string_or_list( - archives_table, - "package-libraries", - "# Which kinds of built libraries to include in the final archives\n", - archives.package_libraries.as_ref(), - ); -} - -fn apply_builds(table: &mut toml_edit::Table, builds: &Option) { - let Some(builds) = builds else { - // Nothing to do. - return; - }; - let Some(builds_table) = table.get_mut("builds") else { - // Nothing to do. - return; - }; - let toml_edit::Item::Table(builds_table) = builds_table else { - panic!("Expected [dist.builds] to be a table"); - }; - - apply_optional_value( - builds_table, - "ssldotcom-windows-sign", - "# Whether we should sign Windows binaries using ssl.com", - builds - .ssldotcom_windows_sign - .as_ref() - .map(|p| p.to_string()), - ); - - apply_optional_value( - builds_table, - "macos-sign", - "# Whether to sign macOS executables\n", - builds.macos_sign, - ); - - apply_cargo_builds(builds_table, builds); - apply_system_dependencies(builds_table, builds.system_dependencies.as_ref()); - - apply_optional_min_glibc_version( - builds_table, - "min-glibc-version", - "# The minimum glibc version supported by the package (overrides auto-detection)\n", - builds.min_glibc_version.as_ref(), - ); - - apply_optional_value( - builds_table, - "omnibor", - "# Whether to use omnibor-cli to generate OmniBOR Artifact IDs\n", - builds.omnibor, - ); - - // Finalize the table - builds_table - .decor_mut() - .set_prefix("\n# Build configuration for dist\n"); -} - -fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) { - if let Some(BoolOr::Bool(b)) = builds.cargo { - // If it was set as a boolean, simply set it as a boolean and return. - apply_optional_value(builds_table, - "cargo", - "# Whether dist should build cargo projects\n# (Use the table format of [dist.builds.cargo] for more nuanced config!)\n", - Some(b), - ); - return; - } - - let Some(BoolOr::Val(ref cargo_builds)) = builds.cargo else { - return; - }; - - let mut possible_table = toml_edit::table(); - let cargo_builds_table = builds_table.get_mut("cargo").unwrap_or(&mut possible_table); - - let toml_edit::Item::Table(cargo_builds_table) = cargo_builds_table else { - panic!("Expected [dist.builds.cargo] to be a table") - }; - - apply_optional_value( - cargo_builds_table, - "rust-toolchain-version", - "# The preferred Rust toolchain to use in CI (rustup toolchain syntax)\n", - cargo_builds.rust_toolchain_version.as_deref(), - ); - - apply_optional_value( - cargo_builds_table, - "msvc-crt-static", - "# Whether +crt-static should be used on msvc\n", - cargo_builds.msvc_crt_static, - ); - - apply_optional_value( - cargo_builds_table, - "precise-builds", - "# Build only the required packages, and individually\n", - cargo_builds.precise_builds, - ); - - apply_string_list( - cargo_builds_table, - "features", - "# Features to pass to cargo build\n", - cargo_builds.features.as_ref(), - ); - - apply_optional_value( - cargo_builds_table, - "default-features", - "# Whether default-features should be enabled with cargo build\n", - cargo_builds.default_features, - ); - - apply_optional_value( - cargo_builds_table, - "all-features", - "# Whether to pass --all-features to cargo build\n", - cargo_builds.all_features, - ); - - apply_optional_value( - cargo_builds_table, - "cargo-auditable", - "# Whether to embed dependency information using cargo-auditable\n", - cargo_builds.cargo_auditable, - ); - - apply_optional_value( - cargo_builds_table, - "cargo-cyclonedx", - "# Whether to use cargo-cyclonedx to generate an SBOM\n", - cargo_builds.cargo_cyclonedx, - ); - - // Finalize the table - cargo_builds_table - .decor_mut() - .set_prefix("\n# How dist should build Cargo projects\n"); -} - -fn apply_system_dependencies( - builds_table: &mut toml_edit::Table, - system_dependencies: Option<&SystemDependencies>, -) { - let Some(system_dependencies) = system_dependencies else { - // Nothing to do. - return; - }; - - // TODO(migration): implement this -} - -fn apply_ci(table: &mut toml_edit::Table, ci: &Option) { - let Some(ci_table) = table.get_mut("ci") else { - // Nothing to do. - return; - }; - let toml_edit::Item::Table(ci_table) = ci_table else { - panic!("Expected [dist.ci] to be a table"); - }; - - apply_ci_common(ci_table, &ci.common); - - if let Some(github) = &ci.github { - match github { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "github", - "# Whether to use GitHub CI\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_ci_github(installers_table, v); - } - } - } - - // Finalize the table - ci_table - .decor_mut() - .set_prefix("\n# CI configuration for dist\n"); -} - -fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { - let Some(gh_table) = ci_table.get_mut("github") else { - return; - }; - let toml_edit::Item::Table(gh_table) = gh_table else { - panic!("Expected [dist.ci.github] to be a table"); - }; - - apply_installers_common(gh_table, &github.common); - - apply_optional_value( - gh_table, - "runners", - "# Custom GitHub runners, specified as target triples\n", - github.runners, - ); - - apply_optional_value( - gh_table, - "permissions", - "# Custom permissions for jobs\n", - github.permissions, - ); - - apply_optional_value( - gh_table, - "build-setup", - "# Custom permissions for jobs\n", - github.build_setup, - ); - - // Finalize the table - gh_table - .decor_mut() - .set_prefix("\n# Configure GitHub CI\n"); -} - - -fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { - apply_optional_value( - table, - "merge-tasks", - "# Whether to run otherwise-parallelizable tasks on the same machine\n", - common.merge_tasks, - ); - - apply_optional_value( - table, - "fail-fast", - "# Whether failing tasks should make us give up on all other tasks\n", - common.fail_fast, - ); - - apply_optional_value( - table, - "cache-builds", - "# Whether builds should try to be cached in CI\n", - common.cache_builds, - ); - - apply_optional_value( - table, - "build-local-artifacts", - "# Whether CI should include auto-generated code to build local artifacts\n", - common.build_local_artifacts, - ); - - apply_optional_value( - table, - "dispatch-releases", - "# Whether CI should trigger releases with dispatches instead of tag pushes\n", - common.dispatch_releases, - ); - - apply_optional_value( - table, - "release-branch", - "# Trigger releases on pushes to this branch instead of tag pushes\n", - common.release_branch.as_ref(), - ); - - apply_optional_value( - table, - "pr-run-mode", - "# Which actions to run on pull requests\n", - common.pr_run_mode.as_ref().map(|m| m.to_string()), - ); - - apply_optional_value( - table, - "tag-namespace", - "# A prefix git tags must include for dist to care about them\n", - common.tag_namespace.as_ref(), - ); - - apply_string_list( - table, - "plan-jobs", - "# Additional plan jobs to run in CI\n", - common.plan_jobs.as_ref(), - ); - - apply_string_list( - table, - "build-local-jobs", - "# Additional local artifacts jobs to run in CI\n", - common.build_local_jobs.as_ref(), - ); - - apply_string_list( - table, - "build-global-jobs", - "# Additional global artifacts jobs to run in CI\n", - common.build_global_jobs.as_ref(), - ); - - apply_string_list( - table, - "host-jobs", - "# Additional hosts jobs to run in CI\n", - common.host_jobs.as_ref(), - ); - - apply_string_list( - table, - "publish-jobs", - "# Additional publish jobs to run in CI\n", - common.publish_jobs.as_ref(), - ); - - apply_string_list( - table, - "post-announce-jobs", - "# Additional jobs to run in CI, after the announce job finishes\n", - common.post_announce_jobs.as_ref(), - ); -} - -fn apply_hosts(table: &mut toml_edit::Table, hosts: &Option) { - let Some(hosts_table) = table.get_mut("hosts") else { - // Nothing to do. - return; - }; - let toml_edit::Item::Table(hosts_table) = hosts_table else { - panic!("Expected [dist.hosts] to be a table"); - }; - - // TODO(migration): implement this - - // Finalize the table - hosts_table - .decor_mut() - .set_prefix("\n# Hosting configuration for dist\n"); -} - -fn apply_installers(table: &mut toml_edit::Table, installers: &Option) { - let Some(installers) = installers else { - return; - }; - let Some(installers_table) = table.get_mut("installers") else { - return; - }; - let toml_edit::Item::Table(installers_table) = installers_table else { - panic!("Expected [dist.installers] to be a table"); - }; - - apply_installers_common(installers_table, &installers.common); - - if let Some(homebrew) = &installers.homebrew { - match homebrew { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "homebrew", - "# Whether to build a Homebrew installer\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_installers_homebrew(installers_table, v); - } - } - } - - if let Some(msi) = &installers.msi { - match msi { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "msi", - "# Whether to build an MSI installer\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_installers_msi(installers_table, v); - } - } - } - - if let Some(npm) = &installers.npm { - match npm { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "npm", - "# Whether to build an NPM installer\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_installers_npm(installers_table, v); - } - } - } - - if let Some(powershell) = &installers.powershell { - match powershell { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "powershell", - "# Whether to build a PowerShell installer\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_installers_powershell(installers_table, v); - } - } - } - - if let Some(shell) = &installers.shell { - match shell { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "shell", - "# Whether to build a Shell installer\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_installers_shell(installers_table, v); - } - } - } - - if let Some(pkg) = &installers.pkg { - match pkg { - BoolOr::Bool(b) => { - apply_optional_value( - installers_table, - "pkg", - "\n# Configuration for the Mac .pkg installer\n", - Some(*b), - ); - } - BoolOr::Val(v) => { - apply_installers_pkg(installers_table, v); - } - } - } - - // installer.updater: Option - // installer.always_use_latest_updater: Option - apply_optional_value( - installers_table, - "updater", - "# Whether to install an updater program alongside the software\n", - installers.updater, - ); - - apply_optional_value( - installers_table, - "always-use-latest-updater", - "# Whether to always use the latest updater version instead of a fixed version\n", - installers.always_use_latest_updater, - ); - - // Finalize the table - installers_table - .decor_mut() - .set_prefix("\n# Installer configuration for dist\n"); -} - -fn apply_installers_common(table: &mut toml_edit::Table, common: &CommonInstallerLayer) { - apply_string_or_list( - table, - "install-path", - "# Path that installers should place binaries in\n", - common.install_path.as_ref(), - ); - - apply_optional_value( - table, - "install-success-msg", - "# Custom message to display on successful install\n", - common.install_success_msg.as_deref(), - ); - - apply_string_or_list( - table, - "install-libraries", - "# Which kinds of packaged libraries to install\n", - common.install_libraries.as_ref(), - ); - - // / Aliases to install binaries as - // TODO(migration): handle `pub bin_aliases: Option>>` -} - -fn apply_installers_homebrew( - installers_table: &mut toml_edit::Table, - homebrew: &HomebrewInstallerLayer, -) { - let Some(homebrew_table) = installers_table.get_mut("homebrew") else { - return; - }; - let toml_edit::Item::Table(homebrew_table) = homebrew_table else { - panic!("Expected [dist.installers.homebrew] to be a table"); - }; - - apply_installers_common(homebrew_table, &homebrew.common); - - apply_optional_value( - homebrew_table, - "tap", - "# A GitHub repo to push Homebrew formulas to\n", - homebrew.tap.clone(), - ); - - apply_optional_value( - homebrew_table, - "formula", - "# Customize the Homebrew formula name\n", - homebrew.formula.clone(), - ); - - // Finalize the table - homebrew_table - .decor_mut() - .set_prefix("\n# Configure the built Homebrew installer\n"); -} - -fn apply_installers_msi(installers_table: &mut toml_edit::Table, msi: &MsiInstallerLayer) { - let Some(msi_table) = installers_table.get_mut("msi") else { - return; - }; - let toml_edit::Item::Table(msi_table) = msi_table else { - panic!("Expected [dist.installers.msi] to be a table"); - }; - - apply_installers_common(msi_table, &msi.common); - - // There are no items under MsiInstallerConfig aside from `msi.common`. - - msi_table - .decor_mut() - .set_prefix("\n# Configure the built MSI installer\n"); -} - -fn apply_installers_npm(installers_table: &mut toml_edit::Table, npm: &NpmInstallerLayer) { - let Some(npm_table) = installers_table.get_mut("npm") else { - return; - }; - let toml_edit::Item::Table(npm_table) = npm_table else { - panic!("Expected [dist.installers.npm] to be a table"); - }; - - apply_installers_common(npm_table, &npm.common); - - apply_optional_value( - npm_table, - "package", - "# The npm package should have this name\n", - npm.package.as_deref(), - ); - - apply_optional_value( - npm_table, - "scope", - "# A namespace to use when publishing this package to the npm registry\n", - npm.scope.as_deref(), - ); -} - -fn apply_installers_powershell( - installers_table: &mut toml_edit::Table, - powershell: &PowershellInstallerLayer, -) { - let Some(powershell_table) = installers_table.get_mut("powershell") else { - return; - }; - let toml_edit::Item::Table(powershell_table) = powershell_table else { - panic!("Expected [dist.installers.powershell] to be a table"); - }; - - apply_installers_common(powershell_table, &powershell.common); - - // TODO(migration): implement this (similar to shell) -} - -fn apply_installers_shell(installers_table: &mut toml_edit::Table, shell: &ShellInstallerLayer) { - let Some(shell_table) = installers_table.get_mut("shell") else { - return; - }; - let toml_edit::Item::Table(shell_table) = shell_table else { - panic!("Expected [dist.installers.shell] to be a table"); - }; - - apply_installers_common(shell_table, &shell.common); - - // TODO(migration): implement this -} - -fn apply_installers_pkg(installers_table: &mut toml_edit::Table, pkg: &PkgInstallerLayer) { - let Some(pkg_table) = installers_table.get_mut("pkg") else { - return; - }; - let toml_edit::Item::Table(pkg_table) = pkg_table else { - panic!("Expected [dist.installers.pkg] to be a table"); - }; - - apply_installers_common(pkg_table, &pkg.common); - - apply_optional_value( - pkg_table, - "identifier", - "# A unique identifier, in tld.domain.package format\n", - pkg.identifier.clone(), - ); - - apply_optional_value( - pkg_table, - "install-location", - "# The location to which software should be installed (defaults to /usr/local)\n", - pkg.install_location.clone(), - ); - - // Finalize the table - pkg_table - .decor_mut() - .set_prefix("\n# Configuration for the Mac .pkg installer\n"); -} - -fn apply_publishers(table: &mut toml_edit::Table, publishers: &Option) { - let Some(publishers_table) = table.get_mut("publishers") else { - return; - }; - let toml_edit::Item::Table(publishers_table) = publishers_table else { - panic!("Expected [dist.publishers] to be a table"); - }; - - // TODO(migration): implement this - - // Finalize the table - publishers_table - .decor_mut() - .set_prefix("\n# Publisher configuration for dist\n"); -} - -/// Update the toml table to add/remove this value -/// -/// If the value is Some we will set the value and hang a description comment off of it. -/// If the given key already existed in the table, this will update it in place and overwrite -/// whatever comment was above it. If the given key is new, it will appear at the end of the -/// table. -/// -/// If the value is None, we delete it (and any comment above it). -fn apply_optional_value(table: &mut toml_edit::Table, key: &str, desc: &str, val: Option) -where - I: Into, -{ - if let Some(val) = val { - table.insert(key, toml_edit::value(val)); - if let Some(mut key) = table.key_mut(key) { - key.leaf_decor_mut().set_prefix(desc) - } - } else { - table.remove(key); - } -} - -/// Same as [`apply_optional_value`][] but with a list of items to `.to_string()` -fn apply_string_list(table: &mut toml_edit::Table, key: &str, desc: &str, list: Option) -where - I: IntoIterator, - I::Item: std::fmt::Display, -{ - if let Some(list) = list { - let items = list.into_iter().map(|i| i.to_string()).collect::>(); - let array: toml_edit::Array = items.into_iter().collect(); - // FIXME: Break the array up into multiple lines with pretty formatting - // if the list is "too long". Alternatively, more precisely toml-edit - // the existing value so that we can preserve the user's formatting and comments. - table.insert(key, toml_edit::Item::Value(toml_edit::Value::Array(array))); - if let Some(mut key) = table.key_mut(key) { - key.leaf_decor_mut().set_prefix(desc) - } - } else { - table.remove(key); - } -} - -/// Same as [`apply_string_list`][] but when the list can be shorthanded as a string -fn apply_string_or_list(table: &mut toml_edit::Table, key: &str, desc: &str, list: Option) -where - I: IntoIterator, - I::Item: std::fmt::Display, -{ - if let Some(list) = list { - let items = list.into_iter().map(|i| i.to_string()).collect::>(); - if items.len() == 1 { - apply_optional_value(table, key, desc, items.into_iter().next()) - } else { - apply_string_list(table, key, desc, Some(items)) - } - } else { - table.remove(key); - } -} - -/// Similar to [`apply_optional_value`][] but specialized to `MinGlibcVersion`, since we're not able to work with structs dynamically -fn apply_optional_min_glibc_version( - table: &mut toml_edit::Table, - key: &str, - desc: &str, - val: Option<&MinGlibcVersion>, -) { - if let Some(min_glibc_version) = val { - let new_item = &mut table[key]; - let mut new_table = toml_edit::table(); - if let Some(new_table) = new_table.as_table_mut() { - for (target, version) in min_glibc_version { - new_table.insert(target, toml_edit::Item::Value(version.to_string().into())); - } - new_table.decor_mut().set_prefix(desc); - } - new_item.or_insert(new_table); - } else { - table.remove(key); - } -} diff --git a/cargo-dist/src/init/apply_dist/artifacts.rs b/cargo-dist/src/init/apply_dist/artifacts.rs new file mode 100644 index 000000000..336e17982 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/artifacts.rs @@ -0,0 +1,100 @@ +use axoasset::toml_edit; +use crate::config::v1::artifacts::ArtifactLayer; +use crate::config::v1::artifacts::archives::ArchiveLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use super::helpers::*; + +pub fn apply(table: &mut toml_edit::Table, artifacts: &Option) { + let Some(artifacts) = artifacts else { + return; + }; + let Some(artifacts_table) = table.get_mut("artifacts") else { + return; + }; + let toml_edit::Item::Table(artifacts_table) = artifacts_table else { + panic!("Expected [dist.artifacts] to be a table"); + }; + + // TODO(migration): implement this + + apply_artifacts_archives(artifacts_table, &artifacts.archives); + + apply_optional_value( + artifacts_table, + "source-tarball", + "# Generate and dist a source tarball\n", + artifacts.source_tarball, + ); + + // TODO(migration): implement dist.artifacts.extra. + /* + apply_optional_value( + artifacts_table, + "extra", + "# Any extra artifacts, and their build scripts\n", + artifacts.extra, + ); + */ + + apply_optional_value( + artifacts_table, + "checksum", + "# The checksum format to generate\n", + artifacts.checksum.map(|cs| cs.to_string()), + ); + + // Finalize the table + artifacts_table + .decor_mut() + .set_prefix("\n# Artifact configuration for dist\n"); +} + +fn apply_artifacts_archives( + artifacts_table: &mut toml_edit::Table, + archives: &Option, +) { + let Some(archives) = archives else { + return; + }; + let Some(archives_table) = artifacts_table.get_mut("archives") else { + return; + }; + let toml_edit::Item::Table(archives_table) = archives_table else { + panic!("Expected [dist.artifacts.archives] to be a table"); + }; + + apply_string_list( + archives_table, + "include", + "# Extra static files to include in each App (path relative to this Cargo.toml's dir)\n", + archives.include.as_ref(), + ); + + apply_optional_value( + archives_table, + "auto-includes", + "# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true)\n", + archives.auto_includes, + ); + + apply_optional_value( + archives_table, + "windows-archive", + "# The archive format to use for windows builds (defaults .zip)\n", + archives.windows_archive.map(|a| a.ext()), + ); + + apply_optional_value( + archives_table, + "unix-archive", + "# The archive format to use for non-windows builds (defaults .tar.xz)\n", + archives.unix_archive.map(|a| a.ext()), + ); + + apply_string_or_list( + archives_table, + "package-libraries", + "# Which kinds of built libraries to include in the final archives\n", + archives.package_libraries.as_ref(), + ); +} diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs new file mode 100644 index 000000000..5ef33ff35 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -0,0 +1,142 @@ +use axoasset::toml_edit; +use crate::config::v1::builds::BuildLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use super::helpers::*; +use super::system_dependencies; + +pub fn apply(table: &mut toml_edit::Table, builds: &Option) { + let Some(builds) = builds else { + // Nothing to do. + return; + }; + let Some(builds_table) = table.get_mut("builds") else { + // Nothing to do. + return; + }; + let toml_edit::Item::Table(builds_table) = builds_table else { + panic!("Expected [dist.builds] to be a table"); + }; + + apply_optional_value( + builds_table, + "ssldotcom-windows-sign", + "# Whether we should sign Windows binaries using ssl.com", + builds + .ssldotcom_windows_sign + .as_ref() + .map(|p| p.to_string()), + ); + + apply_optional_value( + builds_table, + "macos-sign", + "# Whether to sign macOS executables\n", + builds.macos_sign, + ); + + apply_cargo_builds(builds_table, builds); + system_dependencies::apply(builds_table, builds.system_dependencies.as_ref()); + + apply_optional_min_glibc_version( + builds_table, + "min-glibc-version", + "# The minimum glibc version supported by the package (overrides auto-detection)\n", + builds.min_glibc_version.as_ref(), + ); + + apply_optional_value( + builds_table, + "omnibor", + "# Whether to use omnibor-cli to generate OmniBOR Artifact IDs\n", + builds.omnibor, + ); + + // Finalize the table + builds_table + .decor_mut() + .set_prefix("\n# Build configuration for dist\n"); +} + +fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) { + if let Some(BoolOr::Bool(b)) = builds.cargo { + // If it was set as a boolean, simply set it as a boolean and return. + apply_optional_value(builds_table, + "cargo", + "# Whether dist should build cargo projects\n# (Use the table format of [dist.builds.cargo] for more nuanced config!)\n", + Some(b), + ); + return; + } + + let Some(BoolOr::Val(ref cargo_builds)) = builds.cargo else { + return; + }; + + let mut possible_table = toml_edit::table(); + let cargo_builds_table = builds_table.get_mut("cargo").unwrap_or(&mut possible_table); + + let toml_edit::Item::Table(cargo_builds_table) = cargo_builds_table else { + panic!("Expected [dist.builds.cargo] to be a table") + }; + + apply_optional_value( + cargo_builds_table, + "rust-toolchain-version", + "# The preferred Rust toolchain to use in CI (rustup toolchain syntax)\n", + cargo_builds.rust_toolchain_version.as_deref(), + ); + + apply_optional_value( + cargo_builds_table, + "msvc-crt-static", + "# Whether +crt-static should be used on msvc\n", + cargo_builds.msvc_crt_static, + ); + + apply_optional_value( + cargo_builds_table, + "precise-builds", + "# Build only the required packages, and individually\n", + cargo_builds.precise_builds, + ); + + apply_string_list( + cargo_builds_table, + "features", + "# Features to pass to cargo build\n", + cargo_builds.features.as_ref(), + ); + + apply_optional_value( + cargo_builds_table, + "default-features", + "# Whether default-features should be enabled with cargo build\n", + cargo_builds.default_features, + ); + + apply_optional_value( + cargo_builds_table, + "all-features", + "# Whether to pass --all-features to cargo build\n", + cargo_builds.all_features, + ); + + apply_optional_value( + cargo_builds_table, + "cargo-auditable", + "# Whether to embed dependency information using cargo-auditable\n", + cargo_builds.cargo_auditable, + ); + + apply_optional_value( + cargo_builds_table, + "cargo-cyclonedx", + "# Whether to use cargo-cyclonedx to generate an SBOM\n", + cargo_builds.cargo_cyclonedx, + ); + + // Finalize the table + cargo_builds_table + .decor_mut() + .set_prefix("\n# How dist should build Cargo projects\n"); +} diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs new file mode 100644 index 000000000..366fb6d22 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -0,0 +1,184 @@ +use axoasset::toml_edit; +use crate::config::v1::ci::{CiLayer, CommonCiLayer}; +use crate::config::v1::ci::github::GithubCiLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use super::helpers::*; + + +pub fn apply(table: &mut toml_edit::Table, ci: &Option) { + let Some(ci) = ci else { + // Nothing to do. + return; + }; + let Some(ci_table) = table.get_mut("ci") else { + // Nothing to do. + return; + }; + let toml_edit::Item::Table(ci_table) = ci_table else { + panic!("Expected [dist.ci] to be a table"); + }; + + apply_ci_common(ci_table, &ci.common); + + if let Some(github) = &ci.github { + match github { + BoolOr::Bool(b) => { + apply_optional_value( + ci_table, + "github", + "# Whether to use GitHub CI\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_ci_github(ci_table, v); + } + } + } + + // Finalize the table + ci_table + .decor_mut() + .set_prefix("\n# CI configuration for dist\n"); +} + +fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { + let Some(gh_table) = ci_table.get_mut("github") else { + return; + }; + let toml_edit::Item::Table(gh_table) = gh_table else { + panic!("Expected [dist.ci.github] to be a table"); + }; + + apply_ci_common(gh_table, &github.common); + + // FIXME(migration): make these actually compile. + /* + apply_optional_value( + gh_table, + "runners", + "# Custom GitHub runners, specified as target triples\n", + github.runners, + ); + + apply_optional_value( + gh_table, + "permissions", + "# Custom permissions for jobs\n", + github.permissions, + ); + + apply_optional_value( + gh_table, + "build-setup", + "# Custom permissions for jobs\n", + github.build_setup, + ); + */ + + // Finalize the table + gh_table + .decor_mut() + .set_prefix("\n# Configure GitHub CI\n"); +} + + +fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { + apply_optional_value( + table, + "merge-tasks", + "# Whether to run otherwise-parallelizable tasks on the same machine\n", + common.merge_tasks, + ); + + apply_optional_value( + table, + "fail-fast", + "# Whether failing tasks should make us give up on all other tasks\n", + common.fail_fast, + ); + + apply_optional_value( + table, + "cache-builds", + "# Whether builds should try to be cached in CI\n", + common.cache_builds, + ); + + apply_optional_value( + table, + "build-local-artifacts", + "# Whether CI should include auto-generated code to build local artifacts\n", + common.build_local_artifacts, + ); + + apply_optional_value( + table, + "dispatch-releases", + "# Whether CI should trigger releases with dispatches instead of tag pushes\n", + common.dispatch_releases, + ); + + apply_optional_value( + table, + "release-branch", + "# Trigger releases on pushes to this branch instead of tag pushes\n", + common.release_branch.as_ref(), + ); + + apply_optional_value( + table, + "pr-run-mode", + "# Which actions to run on pull requests\n", + common.pr_run_mode.as_ref().map(|m| m.to_string()), + ); + + apply_optional_value( + table, + "tag-namespace", + "# A prefix git tags must include for dist to care about them\n", + common.tag_namespace.as_ref(), + ); + + apply_string_list( + table, + "plan-jobs", + "# Additional plan jobs to run in CI\n", + common.plan_jobs.as_ref(), + ); + + apply_string_list( + table, + "build-local-jobs", + "# Additional local artifacts jobs to run in CI\n", + common.build_local_jobs.as_ref(), + ); + + apply_string_list( + table, + "build-global-jobs", + "# Additional global artifacts jobs to run in CI\n", + common.build_global_jobs.as_ref(), + ); + + apply_string_list( + table, + "host-jobs", + "# Additional hosts jobs to run in CI\n", + common.host_jobs.as_ref(), + ); + + apply_string_list( + table, + "publish-jobs", + "# Additional publish jobs to run in CI\n", + common.publish_jobs.as_ref(), + ); + + apply_string_list( + table, + "post-announce-jobs", + "# Additional jobs to run in CI, after the announce job finishes\n", + common.post_announce_jobs.as_ref(), + ); +} diff --git a/cargo-dist/src/init/apply_dist/helpers.rs b/cargo-dist/src/init/apply_dist/helpers.rs new file mode 100644 index 000000000..e0f390ba9 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/helpers.rs @@ -0,0 +1,85 @@ +use axoasset::toml_edit; +use crate::platform::MinGlibcVersion; + +/// Update the toml table to add/remove this value +/// +/// If the value is Some we will set the value and hang a description comment off of it. +/// If the given key already existed in the table, this will update it in place and overwrite +/// whatever comment was above it. If the given key is new, it will appear at the end of the +/// table. +/// +/// If the value is None, we delete it (and any comment above it). +pub fn apply_optional_value(table: &mut toml_edit::Table, key: &str, desc: &str, val: Option) +where + I: Into, +{ + if let Some(val) = val { + table.insert(key, toml_edit::value(val)); + if let Some(mut key) = table.key_mut(key) { + key.leaf_decor_mut().set_prefix(desc) + } + } else { + table.remove(key); + } +} + +/// Same as [`apply_optional_value`][] but with a list of items to `.to_string()` +pub fn apply_string_list(table: &mut toml_edit::Table, key: &str, desc: &str, list: Option) +where + I: IntoIterator, + I::Item: std::fmt::Display, +{ + if let Some(list) = list { + let items = list.into_iter().map(|i| i.to_string()).collect::>(); + let array: toml_edit::Array = items.into_iter().collect(); + // FIXME: Break the array up into multiple lines with pretty formatting + // if the list is "too long". Alternatively, more precisely toml-edit + // the existing value so that we can preserve the user's formatting and comments. + table.insert(key, toml_edit::Item::Value(toml_edit::Value::Array(array))); + if let Some(mut key) = table.key_mut(key) { + key.leaf_decor_mut().set_prefix(desc) + } + } else { + table.remove(key); + } +} + +/// Same as [`apply_string_list`][] but when the list can be shorthanded as a string +pub fn apply_string_or_list(table: &mut toml_edit::Table, key: &str, desc: &str, list: Option) +where + I: IntoIterator, + I::Item: std::fmt::Display, +{ + if let Some(list) = list { + let items = list.into_iter().map(|i| i.to_string()).collect::>(); + if items.len() == 1 { + apply_optional_value(table, key, desc, items.into_iter().next()) + } else { + apply_string_list(table, key, desc, Some(items)) + } + } else { + table.remove(key); + } +} + +/// Similar to [`apply_optional_value`][] but specialized to `MinGlibcVersion`, since we're not able to work with structs dynamically +pub fn apply_optional_min_glibc_version( + table: &mut toml_edit::Table, + key: &str, + desc: &str, + val: Option<&MinGlibcVersion>, +) { + if let Some(min_glibc_version) = val { + let new_item = &mut table[key]; + let mut new_table = toml_edit::table(); + if let Some(new_table) = new_table.as_table_mut() { + for (target, version) in min_glibc_version { + new_table.insert(target, toml_edit::Item::Value(version.to_string().into())); + } + new_table.decor_mut().set_prefix(desc); + } + new_item.or_insert(new_table); + } else { + table.remove(key); + } +} diff --git a/cargo-dist/src/init/apply_dist/hosts.rs b/cargo-dist/src/init/apply_dist/hosts.rs new file mode 100644 index 000000000..30aa8b397 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/hosts.rs @@ -0,0 +1,22 @@ +use axoasset::toml_edit; +use crate::config::v1::hosts::HostLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use super::helpers::*; + + +pub fn apply(table: &mut toml_edit::Table, hosts: &Option) { + let Some(hosts_table) = table.get_mut("hosts") else { + // Nothing to do. + return; + }; + let toml_edit::Item::Table(hosts_table) = hosts_table else { + panic!("Expected [dist.hosts] to be a table"); + }; + + // TODO(migration): implement this + + // Finalize the table + hosts_table + .decor_mut() + .set_prefix("\n# Hosting configuration for dist\n"); +} diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs new file mode 100644 index 000000000..c75e39986 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -0,0 +1,300 @@ +use axoasset::toml_edit; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use super::helpers::*; + +use crate::config::v1::installers::{ + homebrew::HomebrewInstallerLayer, msi::MsiInstallerLayer, npm::NpmInstallerLayer, + pkg::PkgInstallerLayer, powershell::PowershellInstallerLayer, + shell::ShellInstallerLayer, CommonInstallerLayer, InstallerLayer, +}; + +pub fn apply(table: &mut toml_edit::Table, installers: &Option) { + let Some(installers) = installers else { + return; + }; + let Some(installers_table) = table.get_mut("installers") else { + return; + }; + let toml_edit::Item::Table(installers_table) = installers_table else { + panic!("Expected [dist.installers] to be a table"); + }; + + apply_installers_common(installers_table, &installers.common); + + if let Some(homebrew) = &installers.homebrew { + match homebrew { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "homebrew", + "# Whether to build a Homebrew installer\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_installers_homebrew(installers_table, v); + } + } + } + + if let Some(msi) = &installers.msi { + match msi { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "msi", + "# Whether to build an MSI installer\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_installers_msi(installers_table, v); + } + } + } + + if let Some(npm) = &installers.npm { + match npm { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "npm", + "# Whether to build an NPM installer\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_installers_npm(installers_table, v); + } + } + } + + if let Some(powershell) = &installers.powershell { + match powershell { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "powershell", + "# Whether to build a PowerShell installer\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_installers_powershell(installers_table, v); + } + } + } + + if let Some(shell) = &installers.shell { + match shell { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "shell", + "# Whether to build a Shell installer\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_installers_shell(installers_table, v); + } + } + } + + if let Some(pkg) = &installers.pkg { + match pkg { + BoolOr::Bool(b) => { + apply_optional_value( + installers_table, + "pkg", + "\n# Configuration for the Mac .pkg installer\n", + Some(*b), + ); + } + BoolOr::Val(v) => { + apply_installers_pkg(installers_table, v); + } + } + } + + // installer.updater: Option + // installer.always_use_latest_updater: Option + apply_optional_value( + installers_table, + "updater", + "# Whether to install an updater program alongside the software\n", + installers.updater, + ); + + apply_optional_value( + installers_table, + "always-use-latest-updater", + "# Whether to always use the latest updater version instead of a fixed version\n", + installers.always_use_latest_updater, + ); + + // Finalize the table + installers_table + .decor_mut() + .set_prefix("\n# Installer configuration for dist\n"); +} + +fn apply_installers_common(table: &mut toml_edit::Table, common: &CommonInstallerLayer) { + apply_string_or_list( + table, + "install-path", + "# Path that installers should place binaries in\n", + common.install_path.as_ref(), + ); + + apply_optional_value( + table, + "install-success-msg", + "# Custom message to display on successful install\n", + common.install_success_msg.as_deref(), + ); + + apply_string_or_list( + table, + "install-libraries", + "# Which kinds of packaged libraries to install\n", + common.install_libraries.as_ref(), + ); + + // / Aliases to install binaries as + // TODO(migration): handle `pub bin_aliases: Option>>` +} + +fn apply_installers_homebrew( + installers_table: &mut toml_edit::Table, + homebrew: &HomebrewInstallerLayer, +) { + let Some(homebrew_table) = installers_table.get_mut("homebrew") else { + return; + }; + let toml_edit::Item::Table(homebrew_table) = homebrew_table else { + panic!("Expected [dist.installers.homebrew] to be a table"); + }; + + apply_installers_common(homebrew_table, &homebrew.common); + + apply_optional_value( + homebrew_table, + "tap", + "# A GitHub repo to push Homebrew formulas to\n", + homebrew.tap.clone(), + ); + + apply_optional_value( + homebrew_table, + "formula", + "# Customize the Homebrew formula name\n", + homebrew.formula.clone(), + ); + + // Finalize the table + homebrew_table + .decor_mut() + .set_prefix("\n# Configure the built Homebrew installer\n"); +} + +fn apply_installers_msi(installers_table: &mut toml_edit::Table, msi: &MsiInstallerLayer) { + let Some(msi_table) = installers_table.get_mut("msi") else { + return; + }; + let toml_edit::Item::Table(msi_table) = msi_table else { + panic!("Expected [dist.installers.msi] to be a table"); + }; + + apply_installers_common(msi_table, &msi.common); + + // There are no items under MsiInstallerConfig aside from `msi.common`. + + msi_table + .decor_mut() + .set_prefix("\n# Configure the built MSI installer\n"); +} + +fn apply_installers_npm(installers_table: &mut toml_edit::Table, npm: &NpmInstallerLayer) { + let Some(npm_table) = installers_table.get_mut("npm") else { + return; + }; + let toml_edit::Item::Table(npm_table) = npm_table else { + panic!("Expected [dist.installers.npm] to be a table"); + }; + + apply_installers_common(npm_table, &npm.common); + + apply_optional_value( + npm_table, + "package", + "# The npm package should have this name\n", + npm.package.as_deref(), + ); + + apply_optional_value( + npm_table, + "scope", + "# A namespace to use when publishing this package to the npm registry\n", + npm.scope.as_deref(), + ); +} + +fn apply_installers_powershell( + installers_table: &mut toml_edit::Table, + powershell: &PowershellInstallerLayer, +) { + let Some(powershell_table) = installers_table.get_mut("powershell") else { + return; + }; + let toml_edit::Item::Table(powershell_table) = powershell_table else { + panic!("Expected [dist.installers.powershell] to be a table"); + }; + + apply_installers_common(powershell_table, &powershell.common); + + // TODO(migration): implement this (similar to shell) +} + +fn apply_installers_shell(installers_table: &mut toml_edit::Table, shell: &ShellInstallerLayer) { + let Some(shell_table) = installers_table.get_mut("shell") else { + return; + }; + let toml_edit::Item::Table(shell_table) = shell_table else { + panic!("Expected [dist.installers.shell] to be a table"); + }; + + apply_installers_common(shell_table, &shell.common); + + // TODO(migration): implement this +} + +fn apply_installers_pkg(installers_table: &mut toml_edit::Table, pkg: &PkgInstallerLayer) { + let Some(pkg_table) = installers_table.get_mut("pkg") else { + return; + }; + let toml_edit::Item::Table(pkg_table) = pkg_table else { + panic!("Expected [dist.installers.pkg] to be a table"); + }; + + apply_installers_common(pkg_table, &pkg.common); + + apply_optional_value( + pkg_table, + "identifier", + "# A unique identifier, in tld.domain.package format\n", + pkg.identifier.clone(), + ); + + apply_optional_value( + pkg_table, + "install-location", + "# The location to which software should be installed (defaults to /usr/local)\n", + pkg.install_location.clone(), + ); + + // Finalize the table + pkg_table + .decor_mut() + .set_prefix("\n# Configuration for the Mac .pkg installer\n"); +} diff --git a/cargo-dist/src/init/apply_dist/mod.rs b/cargo-dist/src/init/apply_dist/mod.rs new file mode 100644 index 000000000..d90540123 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/mod.rs @@ -0,0 +1,254 @@ +use axoasset::toml_edit; +use crate::METADATA_DIST; +use crate::config::InstallPathStrategy; +use crate::config::v1::TomlLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::v1::installers::InstallerLayer; + +mod artifacts; +mod builds; +mod ci; +mod helpers; +mod hosts; +mod installers; +mod publishers; +mod system_dependencies; + +use helpers::*; + +/// Update a workspace toml-edit document with the current DistMetadata value +pub fn apply_dist_to_workspace_toml( + workspace_toml: &mut toml_edit::DocumentMut, + meta: &TomlLayer, +) { + let metadata = workspace_toml.as_item_mut(); + apply_dist_to_metadata(metadata, meta); +} + +/// Ensure [dist] has the given values +pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) { + let dist_metadata = &mut metadata[METADATA_DIST]; + + // If there's no table, make one + if !dist_metadata.is_table() { + *dist_metadata = toml_edit::table(); + } + + // Apply formatted/commented values + let table = dist_metadata.as_table_mut().unwrap(); + + // This is intentionally written awkwardly to make you update this + let TomlLayer { + config_version, + dist_version, + dist_url_override, + dist, + allow_dirty, + targets, + artifacts, + builds, + ci, + hosts, + installers, + publishers, + } = &meta; + + let installers = &Some(apply_default_install_path(installers)); + + apply_optional_value( + table, + "config-version", + "# The configuration version to use (valid options: 1)\n", + Some(config_version.to_string()), + ); + + apply_optional_value( + table, + "dist-version", + "# The preferred dist version to use in CI (Cargo.toml SemVer syntax)\n", + dist_version.as_ref().map(|v| v.to_string()), + ); + + apply_optional_value( + table, + "dist-url-override", + "# A URL to use to install `cargo-dist` (with the installer script)\n", + dist_url_override.as_ref().map(|v| v.to_string()), + ); + + apply_optional_value( + table, + "dist", + "# Whether the package should be distributed/built by dist (defaults to true)\n", + *dist, + ); + + apply_string_list( + table, + "allow-dirty", + "# Skip checking whether the specified configuration files are up to date\n", + allow_dirty.as_ref(), + ); + + apply_string_list( + table, + "targets", + "# Target platforms to build apps for (Rust target-triple syntax)\n", + targets.as_ref(), + ); + + artifacts::apply(table, artifacts); + builds::apply(table, builds); + ci::apply(table, ci); + hosts::apply(table, hosts); + installers::apply(table, installers); + publishers::apply(table, publishers); + + // TODO(migration): make sure all of these are handled + /* + + apply_optional_value( + table, + "create-release", + "# Whether dist should create a Github Release or use an existing draft\n", + *create_release, + ); + + apply_optional_value( + table, + "github-release", + "# Which phase dist should use to create the GitHub release\n", + github_release.as_ref().map(|a| a.to_string()), + ); + + apply_optional_value( + table, + "github-releases-repo", + "# Publish GitHub Releases to this repo instead\n", + github_releases_repo.as_ref().map(|a| a.to_string()), + ); + + apply_optional_value( + table, + "github-releases-submodule-path", + "# Read the commit to be tagged from the submodule at this path\n", + github_releases_submodule_path + .as_ref() + .map(|a| a.to_string()), + ); + + apply_string_list( + table, + "local-artifacts-jobs", + "# Local artifacts jobs to run in CI\n", + local_artifacts_jobs.as_ref(), + ); + + apply_string_list( + table, + "global-artifacts-jobs", + "# Global artifacts jobs to run in CI\n", + global_artifacts_jobs.as_ref(), + ); + + apply_string_list( + table, + "host-jobs", + "# Host jobs to run in CI\n", + host_jobs.as_ref(), + ); + + apply_string_list( + table, + "publish-jobs", + "# Publish jobs to run in CI\n", + publish_jobs.as_ref(), + ); + + apply_string_list( + table, + "post-announce-jobs", + "# Post-announce jobs to run in CI\n", + post_announce_jobs.as_ref(), + ); + + apply_optional_value( + table, + "publish-prereleases", + "# Whether to publish prereleases to package managers\n", + *publish_prereleases, + ); + + apply_optional_value( + table, + "force-latest", + "# Always mark releases as latest, ignoring semver semantics\n", + *force_latest, + ); + + apply_optional_value( + table, + "github-attestations", + "# Whether to enable GitHub Attestations\n", + *github_attestations, + ); + + apply_string_or_list( + table, + "hosting", + "# Where to host releases\n", + hosting.as_ref(), + ); + + apply_optional_value( + table, + "install-updater", + "# Whether to install an updater program\n", + *install_updater, + ); + + apply_optional_value( + table, + "always-use-latest-updater", + "# Whether to always use the latest updater instead of a specific known-good version\n", + *always_use_latest_updater, + ); + + apply_optional_value( + table, + "display", + "# Whether to display this app's installers/artifacts in release bodies\n", + *display, + ); + + apply_optional_value( + table, + "display-name", + "# Custom display name to use for this app in release bodies\n", + display_name.as_ref(), + ); + + + + */ + + // Finalize the table + table.decor_mut().set_prefix("\n# Config for 'dist'\n"); +} + +fn apply_default_install_path(installers: &Option) -> InstallerLayer { + let mut installers = installers.clone().unwrap_or_default(); + + // Forcibly inline the default install_path if not specified, + // and if we've specified a shell or powershell installer + let install_path = if installers.common.install_path.is_none() + && !(installers.shell.is_none_or_false() || installers.powershell.is_none_or_false()) + { + Some(InstallPathStrategy::default_list()) + } else { + installers.common.install_path.clone() + }; + + installers.common.install_path = install_path; + installers +} diff --git a/cargo-dist/src/init/apply_dist/publishers.rs b/cargo-dist/src/init/apply_dist/publishers.rs new file mode 100644 index 000000000..423bff1a5 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/publishers.rs @@ -0,0 +1,20 @@ +use axoasset::toml_edit; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::v1::publishers::PublisherLayer; +use super::helpers::*; + +pub fn apply(table: &mut toml_edit::Table, publishers: &Option) { + let Some(publishers_table) = table.get_mut("publishers") else { + return; + }; + let toml_edit::Item::Table(publishers_table) = publishers_table else { + panic!("Expected [dist.publishers] to be a table"); + }; + + // TODO(migration): implement this + + // Finalize the table + publishers_table + .decor_mut() + .set_prefix("\n# Publisher configuration for dist\n"); +} diff --git a/cargo-dist/src/init/apply_dist/system_dependencies.rs b/cargo-dist/src/init/apply_dist/system_dependencies.rs new file mode 100644 index 000000000..218216006 --- /dev/null +++ b/cargo-dist/src/init/apply_dist/system_dependencies.rs @@ -0,0 +1,16 @@ +use axoasset::toml_edit; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::SystemDependencies; +use super::helpers::*; + +pub fn apply( + builds_table: &mut toml_edit::Table, + system_dependencies: Option<&SystemDependencies>, +) { + let Some(system_dependencies) = system_dependencies else { + // Nothing to do. + return; + }; + + // TODO(migration): implement this +} diff --git a/cargo-dist/src/init/mod.rs b/cargo-dist/src/init/mod.rs index f5e7dbb56..7e7da5067 100644 --- a/cargo-dist/src/init/mod.rs +++ b/cargo-dist/src/init/mod.rs @@ -1,8 +1,15 @@ pub(crate) mod v0; pub use v0::do_init; +mod apply_dist; pub mod console_helpers; mod dist_profile; mod init_args; +use console_helpers::theme; +use crate::{do_generate, GenerateArgs}; +use crate::SortedMap; +use crate::config::{self, Config, v1::TomlLayer}; +use crate::errors::DistResult; +use crate::migrate; pub use dist_profile::init_dist_profile; pub use init_args::InitArgs; From bb124a8942b1df7776f32434514661f8448064dc Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Wed, 29 Jan 2025 17:07:36 -0500 Subject: [PATCH 05/26] Finish apply_installers_powershell() and apply_installers_shell(). --- cargo-dist/src/init/apply_dist/installers.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs index c75e39986..4e98d1fb5 100644 --- a/cargo-dist/src/init/apply_dist/installers.rs +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -253,7 +253,10 @@ fn apply_installers_powershell( apply_installers_common(powershell_table, &powershell.common); - // TODO(migration): implement this (similar to shell) + // Finalize the table + installers_table + .decor_mut() + .set_prefix("\n# Configuration for the Windows PowerShell installer\n"); } fn apply_installers_shell(installers_table: &mut toml_edit::Table, shell: &ShellInstallerLayer) { @@ -266,7 +269,10 @@ fn apply_installers_shell(installers_table: &mut toml_edit::Table, shell: &Shell apply_installers_common(shell_table, &shell.common); - // TODO(migration): implement this + // Finalize the table + installers_table + .decor_mut() + .set_prefix("\n# Configuration for the *nix shell installer\n"); } fn apply_installers_pkg(installers_table: &mut toml_edit::Table, pkg: &PkgInstallerLayer) { From 63ca126f9797714799f18ec4f0020c1ea4aab37d Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 30 Jan 2025 12:06:50 -0500 Subject: [PATCH 06/26] resolve a bunch of linter errors --- cargo-dist/src/init/apply_dist/artifacts.rs | 6 +++--- cargo-dist/src/init/apply_dist/builds.rs | 6 +++--- cargo-dist/src/init/apply_dist/ci.rs | 8 +++----- cargo-dist/src/init/apply_dist/helpers.rs | 2 +- cargo-dist/src/init/apply_dist/hosts.rs | 5 ++--- cargo-dist/src/init/apply_dist/installers.rs | 8 ++++---- cargo-dist/src/init/apply_dist/mod.rs | 15 ++++++--------- cargo-dist/src/init/apply_dist/publishers.rs | 4 ++-- .../src/init/apply_dist/system_dependencies.rs | 4 ++-- cargo-dist/src/init/mod.rs | 8 ++++---- 10 files changed, 30 insertions(+), 36 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/artifacts.rs b/cargo-dist/src/init/apply_dist/artifacts.rs index 336e17982..515737fc1 100644 --- a/cargo-dist/src/init/apply_dist/artifacts.rs +++ b/cargo-dist/src/init/apply_dist/artifacts.rs @@ -1,8 +1,8 @@ -use axoasset::toml_edit; -use crate::config::v1::artifacts::ArtifactLayer; +use super::helpers::*; use crate::config::v1::artifacts::archives::ArchiveLayer; +use crate::config::v1::artifacts::ArtifactLayer; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use super::helpers::*; +use axoasset::toml_edit; pub fn apply(table: &mut toml_edit::Table, artifacts: &Option) { let Some(artifacts) = artifacts else { diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs index 5ef33ff35..0c03b0d6c 100644 --- a/cargo-dist/src/init/apply_dist/builds.rs +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -1,8 +1,8 @@ -use axoasset::toml_edit; -use crate::config::v1::builds::BuildLayer; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; use super::helpers::*; use super::system_dependencies; +use crate::config::v1::builds::BuildLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use axoasset::toml_edit; pub fn apply(table: &mut toml_edit::Table, builds: &Option) { let Some(builds) = builds else { diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 366fb6d22..34cbff31e 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -1,9 +1,8 @@ -use axoasset::toml_edit; -use crate::config::v1::ci::{CiLayer, CommonCiLayer}; +use super::helpers::*; use crate::config::v1::ci::github::GithubCiLayer; +use crate::config::v1::ci::{CiLayer, CommonCiLayer}; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use super::helpers::*; - +use axoasset::toml_edit; pub fn apply(table: &mut toml_edit::Table, ci: &Option) { let Some(ci) = ci else { @@ -82,7 +81,6 @@ fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { .set_prefix("\n# Configure GitHub CI\n"); } - fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { apply_optional_value( table, diff --git a/cargo-dist/src/init/apply_dist/helpers.rs b/cargo-dist/src/init/apply_dist/helpers.rs index e0f390ba9..032f17ffd 100644 --- a/cargo-dist/src/init/apply_dist/helpers.rs +++ b/cargo-dist/src/init/apply_dist/helpers.rs @@ -1,5 +1,5 @@ -use axoasset::toml_edit; use crate::platform::MinGlibcVersion; +use axoasset::toml_edit; /// Update the toml table to add/remove this value /// diff --git a/cargo-dist/src/init/apply_dist/hosts.rs b/cargo-dist/src/init/apply_dist/hosts.rs index 30aa8b397..a44277eb4 100644 --- a/cargo-dist/src/init/apply_dist/hosts.rs +++ b/cargo-dist/src/init/apply_dist/hosts.rs @@ -1,8 +1,7 @@ -use axoasset::toml_edit; +use super::helpers::*; use crate::config::v1::hosts::HostLayer; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use super::helpers::*; - +use axoasset::toml_edit; pub fn apply(table: &mut toml_edit::Table, hosts: &Option) { let Some(hosts_table) = table.get_mut("hosts") else { diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs index 4e98d1fb5..30e37274d 100644 --- a/cargo-dist/src/init/apply_dist/installers.rs +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -1,11 +1,11 @@ -use axoasset::toml_edit; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; use super::helpers::*; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use axoasset::toml_edit; use crate::config::v1::installers::{ homebrew::HomebrewInstallerLayer, msi::MsiInstallerLayer, npm::NpmInstallerLayer, - pkg::PkgInstallerLayer, powershell::PowershellInstallerLayer, - shell::ShellInstallerLayer, CommonInstallerLayer, InstallerLayer, + pkg::PkgInstallerLayer, powershell::PowershellInstallerLayer, shell::ShellInstallerLayer, + CommonInstallerLayer, InstallerLayer, }; pub fn apply(table: &mut toml_edit::Table, installers: &Option) { diff --git a/cargo-dist/src/init/apply_dist/mod.rs b/cargo-dist/src/init/apply_dist/mod.rs index d90540123..02512ef92 100644 --- a/cargo-dist/src/init/apply_dist/mod.rs +++ b/cargo-dist/src/init/apply_dist/mod.rs @@ -1,9 +1,9 @@ -use axoasset::toml_edit; -use crate::METADATA_DIST; -use crate::config::InstallPathStrategy; -use crate::config::v1::TomlLayer; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; use crate::config::v1::installers::InstallerLayer; +use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::v1::TomlLayer; +use crate::config::InstallPathStrategy; +use crate::METADATA_DIST; +use axoasset::toml_edit; mod artifacts; mod builds; @@ -17,10 +17,7 @@ mod system_dependencies; use helpers::*; /// Update a workspace toml-edit document with the current DistMetadata value -pub fn apply_dist_to_workspace_toml( - workspace_toml: &mut toml_edit::DocumentMut, - meta: &TomlLayer, -) { +pub fn apply_dist_to_workspace_toml(workspace_toml: &mut toml_edit::DocumentMut, meta: &TomlLayer) { let metadata = workspace_toml.as_item_mut(); apply_dist_to_metadata(metadata, meta); } diff --git a/cargo-dist/src/init/apply_dist/publishers.rs b/cargo-dist/src/init/apply_dist/publishers.rs index 423bff1a5..4b4040371 100644 --- a/cargo-dist/src/init/apply_dist/publishers.rs +++ b/cargo-dist/src/init/apply_dist/publishers.rs @@ -1,7 +1,7 @@ -use axoasset::toml_edit; +use super::helpers::*; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; use crate::config::v1::publishers::PublisherLayer; -use super::helpers::*; +use axoasset::toml_edit; pub fn apply(table: &mut toml_edit::Table, publishers: &Option) { let Some(publishers_table) = table.get_mut("publishers") else { diff --git a/cargo-dist/src/init/apply_dist/system_dependencies.rs b/cargo-dist/src/init/apply_dist/system_dependencies.rs index 218216006..84f7687d5 100644 --- a/cargo-dist/src/init/apply_dist/system_dependencies.rs +++ b/cargo-dist/src/init/apply_dist/system_dependencies.rs @@ -1,7 +1,7 @@ -use axoasset::toml_edit; +use super::helpers::*; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; use crate::config::SystemDependencies; -use super::helpers::*; +use axoasset::toml_edit; pub fn apply( builds_table: &mut toml_edit::Table, diff --git a/cargo-dist/src/init/mod.rs b/cargo-dist/src/init/mod.rs index 7e7da5067..b8324e992 100644 --- a/cargo-dist/src/init/mod.rs +++ b/cargo-dist/src/init/mod.rs @@ -5,11 +5,11 @@ pub mod console_helpers; mod dist_profile; mod init_args; -use console_helpers::theme; -use crate::{do_generate, GenerateArgs}; -use crate::SortedMap; -use crate::config::{self, Config, v1::TomlLayer}; +use crate::config::{self, v1::TomlLayer, Config}; use crate::errors::DistResult; use crate::migrate; +use crate::SortedMap; +use crate::{do_generate, GenerateArgs}; +use console_helpers::theme; pub use dist_profile::init_dist_profile; pub use init_args::InitArgs; From c70be859e69c0d4e2b85d39fdd68600c9df78bc4 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 30 Jan 2025 12:32:36 -0500 Subject: [PATCH 07/26] improve phrasing of comments in generated configs --- cargo-dist/src/init/apply_dist/ci.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 34cbff31e..1e876ddd6 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -25,7 +25,7 @@ pub fn apply(table: &mut toml_edit::Table, ci: &Option) { apply_optional_value( ci_table, "github", - "# Whether to use GitHub CI\n", + "# Whether dist should generate workflows for GitHub CI\n", Some(*b), ); } @@ -78,7 +78,7 @@ fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { // Finalize the table gh_table .decor_mut() - .set_prefix("\n# Configure GitHub CI\n"); + .set_prefix("\n# Configure generated workflows for GitHub CI\n"); } fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { From 15ec92ad2b207b59bcfb0cabbc786305382fd1b6 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 30 Jan 2025 18:39:02 -0500 Subject: [PATCH 08/26] add tests for init/apply_dist/ci --- cargo-dist/src/init/apply_dist/ci.rs | 195 +++++++++++++++++++++++++-- 1 file changed, 181 insertions(+), 14 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 1e876ddd6..385a16ddb 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -2,20 +2,18 @@ use super::helpers::*; use crate::config::v1::ci::github::GithubCiLayer; use crate::config::v1::ci::{CiLayer, CommonCiLayer}; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit; +use axoasset::toml_edit::{self, DocumentMut, Item, Table}; pub fn apply(table: &mut toml_edit::Table, ci: &Option) { let Some(ci) = ci else { // Nothing to do. return; }; - let Some(ci_table) = table.get_mut("ci") else { - // Nothing to do. - return; - }; - let toml_edit::Item::Table(ci_table) = ci_table else { - panic!("Expected [dist.ci] to be a table"); - }; + let ci_table = table + .entry("ci") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.ci] should be a table"); apply_ci_common(ci_table, &ci.common); @@ -42,12 +40,11 @@ pub fn apply(table: &mut toml_edit::Table, ci: &Option) { } fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { - let Some(gh_table) = ci_table.get_mut("github") else { - return; - }; - let toml_edit::Item::Table(gh_table) = gh_table else { - panic!("Expected [dist.ci.github] to be a table"); - }; + let gh_table = ci_table + .entry("github") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.ci.github] should be a table"); apply_ci_common(gh_table, &github.common); @@ -180,3 +177,173 @@ fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { common.post_announce_jobs.as_ref(), ); } + +#[cfg(test)] +mod test { + use super::*; + use crate::config::JobStyle; + use miette::IntoDiagnostic; + + fn source() -> toml_edit::DocumentMut { + let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); + let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); + doc + } + + // Given a DocumentMut, make sure it has a [dist] table, and return + // a reference to that dist table. + fn dist_table(doc: &mut toml_edit::DocumentMut) -> &mut toml_edit::Table { + let dist = doc + .entry("dist") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .unwrap(); + // Don't show the empty top-level [dist]. + dist.set_implicit(true); + // Return the table we just created. + dist + } + + #[test] + fn apply_ci_empty() { + let expected = ""; + + let ci = Some(CiLayer { + common: CommonCiLayer { + merge_tasks: None, + fail_fast: None, + cache_builds: None, + build_local_artifacts: None, + dispatch_releases: None, + release_branch: None, + pr_run_mode: None, + tag_namespace: None, + plan_jobs: None, + build_local_jobs: None, + build_global_jobs: None, + host_jobs: None, + publish_jobs: None, + post_announce_jobs: None, + }, + github: None, + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &ci); + + let toml_text = table.to_string(); + assert_eq!(toml_text, expected); + } + + #[test] + fn apply_ci_everything() { + let expected = r#" +# CI configuration for dist +[dist.ci] +# Whether to run otherwise-parallelizable tasks on the same machine +merge-tasks = true +# Whether failing tasks should make us give up on all other tasks +fail-fast = true +# Whether builds should try to be cached in CI +cache-builds = true +# Whether CI should include auto-generated code to build local artifacts +build-local-artifacts = true +# Whether CI should trigger releases with dispatches instead of tag pushes +dispatch-releases = true +# Trigger releases on pushes to this branch instead of tag pushes +release-branch = "main" +# Which actions to run on pull requests +pr-run-mode = "skip" +# A prefix git tags must include for dist to care about them +tag-namespace = "some-namespace" +# Additional plan jobs to run in CI +plan-jobs = ["./plan-job"] +# Additional local artifacts jobs to run in CI +build-local-jobs = ["./build-local-job-1", "./build-local-job-2"] +# Additional global artifacts jobs to run in CI +build-global-jobs = ["./build-global-job"] +# Additional hosts jobs to run in CI +host-jobs = ["./host-job"] +# Additional publish jobs to run in CI +publish-jobs = ["./publish-job"] +# Additional jobs to run in CI, after the announce job finishes +post-announce-jobs = ["./post-announce-job"] +# Whether dist should generate workflows for GitHub CI +github = true +"#; + + let ci = Some(CiLayer { + common: CommonCiLayer { + merge_tasks: Some(true), + fail_fast: Some(true), + cache_builds: Some(true), + build_local_artifacts: Some(true), + dispatch_releases: Some(true), + release_branch: Some("main".to_string()), + pr_run_mode: Some(dist_schema::PrRunMode::Skip), + tag_namespace: Some("some-namespace".to_string()), + plan_jobs: Some(vec![ + "./plan-job".parse().unwrap(), + ]), + build_local_jobs: Some(vec![ + "./build-local-job-1".parse().unwrap(), + "./build-local-job-2".parse().unwrap(), + ]), + build_global_jobs: Some(vec![ + "./build-global-job".parse().unwrap(), + ]), + host_jobs: Some(vec![ + "./host-job".parse().unwrap(), + ]), + publish_jobs: Some(vec![ + "./publish-job".parse().unwrap(), + ]), + post_announce_jobs: Some(vec![ + "./post-announce-job".parse().unwrap(), + ]), + }, + github: Some(BoolOr::Bool(true)), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &ci); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } + + #[test] + fn apply_ci_gh_complex() { + let expected = r#" +# CI configuration for dist +[dist.ci] + +# Configure generated workflows for GitHub CI +[dist.ci.github] +build-setup = "build-setup" +"#; + + let ci = Some(CiLayer { + common: CommonCiLayer::default(), + github: Some(BoolOr::Val(GithubCiLayer { + common: CommonCiLayer::default(), + build_setup: Some("build-setup".to_string()), + permissions: None, + runners: None, + })), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &ci); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + + } +} From d8de1be368518d9eb77e3028313022934c2fc2ba Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Wed, 5 Feb 2025 15:14:28 -0500 Subject: [PATCH 09/26] fix cargo-fmt warnings --- cargo-dist/src/init/apply_dist/ci.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 385a16ddb..004e87e18 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -284,25 +284,15 @@ github = true release_branch: Some("main".to_string()), pr_run_mode: Some(dist_schema::PrRunMode::Skip), tag_namespace: Some("some-namespace".to_string()), - plan_jobs: Some(vec![ - "./plan-job".parse().unwrap(), - ]), + plan_jobs: Some(vec!["./plan-job".parse().unwrap()]), build_local_jobs: Some(vec![ "./build-local-job-1".parse().unwrap(), "./build-local-job-2".parse().unwrap(), ]), - build_global_jobs: Some(vec![ - "./build-global-job".parse().unwrap(), - ]), - host_jobs: Some(vec![ - "./host-job".parse().unwrap(), - ]), - publish_jobs: Some(vec![ - "./publish-job".parse().unwrap(), - ]), - post_announce_jobs: Some(vec![ - "./post-announce-job".parse().unwrap(), - ]), + build_global_jobs: Some(vec!["./build-global-job".parse().unwrap()]), + host_jobs: Some(vec!["./host-job".parse().unwrap()]), + publish_jobs: Some(vec!["./publish-job".parse().unwrap()]), + post_announce_jobs: Some(vec!["./post-announce-job".parse().unwrap()]), }, github: Some(BoolOr::Bool(true)), }); @@ -344,6 +334,5 @@ build-setup = "build-setup" let toml_text = doc.to_string(); assert_eq!(expected, toml_text); - } } From 472ea4331152efaf5f16578f30377cfcbfeb109c Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Fri, 7 Feb 2025 16:34:17 -0500 Subject: [PATCH 10/26] Add pretty_assertions as a dev dependency. --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 1 + cargo-dist/Cargo.toml | 1 + 3 files changed, 25 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c33de305b..cf79afa76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -746,6 +746,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -803,6 +809,7 @@ dependencies = [ "miette 7.5.0", "minijinja", "newline-converter", + "pretty_assertions", "schemars", "semver", "serde", @@ -2068,6 +2075,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.83" @@ -3658,6 +3675,12 @@ dependencies = [ "lzma-sys", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yoke" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index f0a01a1f1..71c32cabb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ current_platform = "0.2.0" color-backtrace = "0.6.1" backtrace = "0.3.74" target-lexicon = "0.12.16" +pretty_assertions = "1.4.1" [workspace.metadata.release] shared-version = true diff --git a/cargo-dist/Cargo.toml b/cargo-dist/Cargo.toml index e671e1362..6541f2778 100644 --- a/cargo-dist/Cargo.toml +++ b/cargo-dist/Cargo.toml @@ -78,6 +78,7 @@ schemars.workspace = true insta.workspace = true tar.workspace = true flate2.workspace = true +pretty_assertions.workspace = true [package.metadata.dist] features = ["fear_no_msrv", "tls_native_roots"] From 716f2cc708db40ae2bbd39151f46a9d665eecc40 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Fri, 7 Feb 2025 16:34:45 -0500 Subject: [PATCH 11/26] Finish apply_dist::artifacts::apply() --- cargo-dist/src/init/apply_dist/artifacts.rs | 126 +++++++++++++++++--- 1 file changed, 109 insertions(+), 17 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/artifacts.rs b/cargo-dist/src/init/apply_dist/artifacts.rs index 515737fc1..e79b70d71 100644 --- a/cargo-dist/src/init/apply_dist/artifacts.rs +++ b/cargo-dist/src/init/apply_dist/artifacts.rs @@ -2,31 +2,29 @@ use super::helpers::*; use crate::config::v1::artifacts::archives::ArchiveLayer; use crate::config::v1::artifacts::ArtifactLayer; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit; +use axoasset::toml_edit::{self, DocumentMut, Item, Table}; pub fn apply(table: &mut toml_edit::Table, artifacts: &Option) { let Some(artifacts) = artifacts else { return; }; - let Some(artifacts_table) = table.get_mut("artifacts") else { - return; - }; - let toml_edit::Item::Table(artifacts_table) = artifacts_table else { - panic!("Expected [dist.artifacts] to be a table"); - }; - - // TODO(migration): implement this + let artifacts_table = table + .entry("artifacts") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.artifacts] should be a table"); apply_artifacts_archives(artifacts_table, &artifacts.archives); apply_optional_value( artifacts_table, "source-tarball", - "# Generate and dist a source tarball\n", + "# Generate a source tarball\n", artifacts.source_tarball, ); - // TODO(migration): implement dist.artifacts.extra. + // FIXME(migration): Like with the v0 config, [dist.artifacts.extra] is + // not currently reformatted due to compelxity. /* apply_optional_value( artifacts_table, @@ -56,12 +54,11 @@ fn apply_artifacts_archives( let Some(archives) = archives else { return; }; - let Some(archives_table) = artifacts_table.get_mut("archives") else { - return; - }; - let toml_edit::Item::Table(archives_table) = archives_table else { - panic!("Expected [dist.artifacts.archives] to be a table"); - }; + let archives_table = artifacts_table + .entry("archives") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.artifacts.archives] should be a table"); apply_string_list( archives_table, @@ -98,3 +95,98 @@ fn apply_artifacts_archives( archives.package_libraries.as_ref(), ); } + +#[cfg(test)] +mod test { + use super::*; + use crate::{CompressionImpl, ChecksumStyle, ZipStyle}; + use crate::config::LibraryStyle; + use axoasset::toml_edit::{self, DocumentMut, Item, Table}; + use miette::IntoDiagnostic; + use pretty_assertions::{assert_eq, assert_ne}; + + fn source() -> toml_edit::DocumentMut { + let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); + let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); + doc + } + + // Given a DocumentMut, make sure it has a [dist] table, and return + // a reference to that dist table. + fn dist_table(doc: &mut toml_edit::DocumentMut) -> &mut toml_edit::Table { + let dist = doc + .entry("dist") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .unwrap(); + // Don't show the empty top-level [dist]. + dist.set_implicit(true); + // Return the table we just created. + dist + } + + #[test] + fn apply_artifacts_empty() { + let expected = ""; + + let artifacts = Some(ArtifactLayer { + archives: None, + checksum: None, + extra: None, + source_tarball: None, + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &artifacts); + + let toml_text = table.to_string(); + assert_eq!(toml_text, expected); + } + + #[test] + fn apply_artifacts_everything() { + let expected = r#" +# Artifact configuration for dist +[dist.artifacts] +# Generate a source tarball +source-tarball = false +# The checksum format to generate +checksum = "blake2b" + +[dist.artifacts.archives] +# Extra static files to include in each App (path relative to this Cargo.toml's dir) +include = ["some-include"] +# Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true) +auto-includes = false +# The archive format to use for windows builds (defaults .zip) +windows-archive = ".tar.gz" +# The archive format to use for non-windows builds (defaults .tar.xz) +unix-archive = ".zip" +# Which kinds of built libraries to include in the final archives +package-libraries = ["cdylib", "cstaticlib"] +"#; + + let artifacts = Some(ArtifactLayer { + archives: Some(ArchiveLayer { + include: Some(vec!["some-include".into()]), + auto_includes: Some(false), + windows_archive: Some(ZipStyle::Tar(CompressionImpl::Gzip)), + unix_archive: Some(ZipStyle::Zip), + package_libraries: Some(vec![LibraryStyle::CDynamic, LibraryStyle::CStatic]), + }), + checksum: Some(ChecksumStyle::Blake2b), + extra: None, + source_tarball: Some(false), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &artifacts); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } +} From 67eeb3d40df3b04a865ecd25083ec90a51b1a702 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Fri, 7 Feb 2025 16:35:01 -0500 Subject: [PATCH 12/26] Finish apply_dist::ci::apply() --- cargo-dist/src/init/apply_dist/ci.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 004e87e18..0f5d8bea4 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -48,7 +48,8 @@ fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { apply_ci_common(gh_table, &github.common); - // FIXME(migration): make these actually compile. + // FIXME(migration): Like with the v0 config, [dist.github.runners] and + // [dist.github.permissions] are not currently reformatted due to complexity. /* apply_optional_value( gh_table, @@ -63,14 +64,15 @@ fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { "# Custom permissions for jobs\n", github.permissions, ); + */ apply_optional_value( gh_table, "build-setup", - "# Custom permissions for jobs\n", - github.build_setup, + "# Path to a file containing a YAML array of steps, to be performed before 'dist build'\n\ + # KNOWN BUG: https://github.com/axodotdev/cargo-dist/issues/1750\n", + github.build_setup.clone(), ); - */ // Finalize the table gh_table @@ -314,14 +316,16 @@ github = true # Configure generated workflows for GitHub CI [dist.ci.github] -build-setup = "build-setup" +# Path to a file containing a YAML array of steps, to be performed before 'dist build' +# KNOWN BUG: https://github.com/axodotdev/cargo-dist/issues/1750 +build-setup = "some-build-setup" "#; let ci = Some(CiLayer { common: CommonCiLayer::default(), github: Some(BoolOr::Val(GithubCiLayer { common: CommonCiLayer::default(), - build_setup: Some("build-setup".to_string()), + build_setup: Some("some-build-setup".to_string()), permissions: None, runners: None, })), From ffd3d11d33f3489493d6ac6931bfccf4bb1443ba Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Fri, 7 Feb 2025 16:35:14 -0500 Subject: [PATCH 13/26] Finish apply_dist::installers::apply() --- cargo-dist/src/init/apply_dist/installers.rs | 302 ++++++++++++++++--- 1 file changed, 254 insertions(+), 48 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs index 30e37274d..f114e3b84 100644 --- a/cargo-dist/src/init/apply_dist/installers.rs +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -1,6 +1,6 @@ use super::helpers::*; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit; +use axoasset::toml_edit::{self, DocumentMut, Item, Table}; use crate::config::v1::installers::{ homebrew::HomebrewInstallerLayer, msi::MsiInstallerLayer, npm::NpmInstallerLayer, @@ -12,12 +12,11 @@ pub fn apply(table: &mut toml_edit::Table, installers: &Option) let Some(installers) = installers else { return; }; - let Some(installers_table) = table.get_mut("installers") else { - return; - }; - let toml_edit::Item::Table(installers_table) = installers_table else { - panic!("Expected [dist.installers] to be a table"); - }; + let installers_table = table + .entry("installers") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers] should be a table"); apply_installers_common(installers_table, &installers.common); @@ -107,7 +106,7 @@ pub fn apply(table: &mut toml_edit::Table, installers: &Option) apply_optional_value( installers_table, "pkg", - "\n# Configuration for the Mac .pkg installer\n", + "# Whether to build a Mac .pkg installer\n", Some(*b), ); } @@ -169,12 +168,11 @@ fn apply_installers_homebrew( installers_table: &mut toml_edit::Table, homebrew: &HomebrewInstallerLayer, ) { - let Some(homebrew_table) = installers_table.get_mut("homebrew") else { - return; - }; - let toml_edit::Item::Table(homebrew_table) = homebrew_table else { - panic!("Expected [dist.installers.homebrew] to be a table"); - }; + let homebrew_table = installers_table + .entry("homebrew") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers.homebrew] should be a table"); apply_installers_common(homebrew_table, &homebrew.common); @@ -195,16 +193,15 @@ fn apply_installers_homebrew( // Finalize the table homebrew_table .decor_mut() - .set_prefix("\n# Configure the built Homebrew installer\n"); + .set_prefix("\n# Configuration for the Homebrew installer\n"); } fn apply_installers_msi(installers_table: &mut toml_edit::Table, msi: &MsiInstallerLayer) { - let Some(msi_table) = installers_table.get_mut("msi") else { - return; - }; - let toml_edit::Item::Table(msi_table) = msi_table else { - panic!("Expected [dist.installers.msi] to be a table"); - }; + let msi_table = installers_table + .entry("msi") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers.msi] should be a table"); apply_installers_common(msi_table, &msi.common); @@ -212,44 +209,46 @@ fn apply_installers_msi(installers_table: &mut toml_edit::Table, msi: &MsiInstal msi_table .decor_mut() - .set_prefix("\n# Configure the built MSI installer\n"); + .set_prefix("\n# Configuration for the MSI installer\n"); } fn apply_installers_npm(installers_table: &mut toml_edit::Table, npm: &NpmInstallerLayer) { - let Some(npm_table) = installers_table.get_mut("npm") else { - return; - }; - let toml_edit::Item::Table(npm_table) = npm_table else { - panic!("Expected [dist.installers.npm] to be a table"); - }; + let npm_table = installers_table + .entry("npm") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers.npm] should be a table"); apply_installers_common(npm_table, &npm.common); apply_optional_value( npm_table, "package", - "# The npm package should have this name\n", + "# The name of the npm package\n", npm.package.as_deref(), ); apply_optional_value( npm_table, "scope", - "# A namespace to use when publishing this package to the npm registry\n", + "# The namespace to use when publishing this package to the npm registry\n", npm.scope.as_deref(), ); + + npm_table + .decor_mut() + .set_prefix("\n# Configuration for the NPM installer\n"); } fn apply_installers_powershell( installers_table: &mut toml_edit::Table, powershell: &PowershellInstallerLayer, ) { - let Some(powershell_table) = installers_table.get_mut("powershell") else { - return; - }; - let toml_edit::Item::Table(powershell_table) = powershell_table else { - panic!("Expected [dist.installers.powershell] to be a table"); - }; + let powershell_table = installers_table + .entry("powershell") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers.powershell] should be a table"); apply_installers_common(powershell_table, &powershell.common); @@ -260,12 +259,11 @@ fn apply_installers_powershell( } fn apply_installers_shell(installers_table: &mut toml_edit::Table, shell: &ShellInstallerLayer) { - let Some(shell_table) = installers_table.get_mut("shell") else { - return; - }; - let toml_edit::Item::Table(shell_table) = shell_table else { - panic!("Expected [dist.installers.shell] to be a table"); - }; + let shell_table = installers_table + .entry("shell") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers.shell] should be a table"); apply_installers_common(shell_table, &shell.common); @@ -276,12 +274,11 @@ fn apply_installers_shell(installers_table: &mut toml_edit::Table, shell: &Shell } fn apply_installers_pkg(installers_table: &mut toml_edit::Table, pkg: &PkgInstallerLayer) { - let Some(pkg_table) = installers_table.get_mut("pkg") else { - return; - }; - let toml_edit::Item::Table(pkg_table) = pkg_table else { - panic!("Expected [dist.installers.pkg] to be a table"); - }; + let pkg_table = installers_table + .entry("pkg") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.installers.pkg] should be a table"); apply_installers_common(pkg_table, &pkg.common); @@ -304,3 +301,212 @@ fn apply_installers_pkg(installers_table: &mut toml_edit::Table, pkg: &PkgInstal .decor_mut() .set_prefix("\n# Configuration for the Mac .pkg installer\n"); } + +#[cfg(test)] +mod test { + use super::*; + use crate::{CompressionImpl, ChecksumStyle, ZipStyle}; + use crate::config::LibraryStyle; + use crate::init::apply_dist::InstallPathStrategy; + use miette::IntoDiagnostic; + use pretty_assertions::{assert_eq, assert_ne}; + + fn source() -> toml_edit::DocumentMut { + let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); + let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); + doc + } + + // Given a DocumentMut, make sure it has a [dist] table, and return + // a reference to that dist table. + fn dist_table(doc: &mut toml_edit::DocumentMut) -> &mut toml_edit::Table { + let dist = doc + .entry("dist") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .unwrap(); + // Don't show the empty top-level [dist]. + dist.set_implicit(true); + // Return the table we just created. + dist + } + + #[test] + fn apply_installers_empty() { + let expected = ""; + + let installers = Some(InstallerLayer { + common: CommonInstallerLayer { + install_path: None, + install_success_msg: None, + install_libraries: None, + bin_aliases: None, + }, + homebrew: None, + msi: None, + npm: None, + powershell: None, + shell: None, + pkg: None, + updater: None, + always_use_latest_updater: None, + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &installers); + + let toml_text = table.to_string(); + assert_eq!(toml_text, expected); + } + + #[test] + fn apply_installers_everything_bools() { + let expected = r#" +# Installer configuration for dist +[dist.installers] +# Path that installers should place binaries in +install-path = ["~/some-install-path/", "CARGO_HOME"] +# Custom message to display on successful install +install-success-msg = "default success message" +# Which kinds of packaged libraries to install +install-libraries = ["cdylib", "cstaticlib"] +# Whether to build a Homebrew installer +homebrew = true +# Whether to build an MSI installer +msi = true +# Whether to build an NPM installer +npm = true +# Whether to build a PowerShell installer +powershell = true +# Whether to build a Shell installer +shell = true +# Whether to build a Mac .pkg installer +pkg = true +# Whether to install an updater program alongside the software +updater = true +# Whether to always use the latest updater version instead of a fixed version +always-use-latest-updater = true +"#; + + let installers = Some(InstallerLayer { + common: CommonInstallerLayer { + install_path: Some(vec![ + InstallPathStrategy::HomeSubdir { + subdir: "some-install-path/".to_string(), + }, + InstallPathStrategy::CargoHome, + ]), + install_success_msg: Some("default success message".to_string()), + install_libraries: Some(vec![LibraryStyle::CDynamic, LibraryStyle::CStatic]), + bin_aliases: None, + }, + homebrew: Some(BoolOr::Bool(true)), + msi: Some(BoolOr::Bool(true)), + npm: Some(BoolOr::Bool(true)), + powershell: Some(BoolOr::Bool(true)), + shell: Some(BoolOr::Bool(true)), + pkg: Some(BoolOr::Bool(true)), + updater: Some(true), + always_use_latest_updater: Some(true), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &installers); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } + + + #[test] + fn apply_installers_complex() { + let expected = r#" +# Installer configuration for dist +[dist.installers] +# Path that installers should place binaries in +install-path = ["~/some-install-path/", "CARGO_HOME"] +# Custom message to display on successful install +install-success-msg = "default success message" +# Which kinds of packaged libraries to install +install-libraries = ["cdylib", "cstaticlib"] +# Whether to build an MSI installer +msi = true +# Whether to build a PowerShell installer +powershell = true +# Whether to build a Shell installer +shell = true +# Whether to install an updater program alongside the software +updater = true +# Whether to always use the latest updater version instead of a fixed version +always-use-latest-updater = true + +# Configuration for the Homebrew installer +[dist.installers.homebrew] +# A GitHub repo to push Homebrew formulas to +tap = "homebrew-tap" +# Customize the Homebrew formula name +formula = "homebrew-formula" + +# Configuration for the NPM installer +[dist.installers.npm] +# The name of the npm package +package = "npm-package" +# The namespace to use when publishing this package to the npm registry +scope = "npm-scope" + +# Configuration for the Mac .pkg installer +[dist.installers.pkg] +# A unique identifier, in tld.domain.package format +identifier = "pkg-identifier" +# The location to which software should be installed (defaults to /usr/local) +install-location = "pkg-install-location" +"#; + + + let installers = Some(InstallerLayer { + common: CommonInstallerLayer { + install_path: Some(vec![ + InstallPathStrategy::HomeSubdir { + subdir: "some-install-path/".to_string(), + }, + InstallPathStrategy::CargoHome, + ]), + install_success_msg: Some("default success message".to_string()), + install_libraries: Some(vec![LibraryStyle::CDynamic, LibraryStyle::CStatic]), + bin_aliases: None, + }, + homebrew: Some(BoolOr::Val(HomebrewInstallerLayer { + common: CommonInstallerLayer::default(), + tap: Some("homebrew-tap".to_string()), + formula: Some("homebrew-formula".to_string()), + })), + msi: Some(BoolOr::Bool(true)), + npm: Some(BoolOr::Val(NpmInstallerLayer { + common: CommonInstallerLayer::default(), + package: Some("npm-package".to_string()), + scope: Some("npm-scope".to_string()), + })), + powershell: Some(BoolOr::Bool(true)), + shell: Some(BoolOr::Bool(true)), + pkg: Some(BoolOr::Val(PkgInstallerLayer { + common: CommonInstallerLayer::default(), + identifier: Some("pkg-identifier".to_string()), + install_location: Some("pkg-install-location".to_string()), + })), + updater: Some(true), + always_use_latest_updater: Some(true), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &installers); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } +} From 9472ee98e6d37c6643af88e9a665bcac32fcfabb Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Mon, 10 Feb 2025 13:42:46 -0500 Subject: [PATCH 14/26] Add skip_optional_value() and skip_string_list(). --- cargo-dist/src/init/apply_dist/helpers.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cargo-dist/src/init/apply_dist/helpers.rs b/cargo-dist/src/init/apply_dist/helpers.rs index 032f17ffd..c9a697d34 100644 --- a/cargo-dist/src/init/apply_dist/helpers.rs +++ b/cargo-dist/src/init/apply_dist/helpers.rs @@ -1,5 +1,19 @@ use crate::platform::MinGlibcVersion; use axoasset::toml_edit; +use tracing::trace; + +pub fn skip_optional_value( + _table: &mut toml_edit::Table, + key: &str, + _desc: &str, + _val: Option, +) { + trace!("apply_dist/skipping: {}", key); +} + +pub fn skip_string_list(_table: &mut toml_edit::Table, key: &str, desc: &str, _list: Option) { + trace!("apply_dist/skipping: {}", key); +} /// Update the toml table to add/remove this value /// From 32c1a089cd41e5b354a96a380b895c59da6cb5ed Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Mon, 10 Feb 2025 13:51:49 -0500 Subject: [PATCH 15/26] Actually use skip_optional_value() and skip_string_list(). --- cargo-dist/src/init/apply_dist/artifacts.rs | 9 +++------ cargo-dist/src/init/apply_dist/ci.rs | 14 ++++++-------- cargo-dist/src/init/apply_dist/installers.rs | 9 +++++++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/artifacts.rs b/cargo-dist/src/init/apply_dist/artifacts.rs index e79b70d71..04efd3105 100644 --- a/cargo-dist/src/init/apply_dist/artifacts.rs +++ b/cargo-dist/src/init/apply_dist/artifacts.rs @@ -23,16 +23,13 @@ pub fn apply(table: &mut toml_edit::Table, artifacts: &Option) { artifacts.source_tarball, ); - // FIXME(migration): Like with the v0 config, [dist.artifacts.extra] is - // not currently reformatted due to compelxity. - /* - apply_optional_value( + // [dist.artifacts.extra] is not reformatted due to complexity. + skip_optional_value( artifacts_table, "extra", "# Any extra artifacts, and their build scripts\n", - artifacts.extra, + artifacts.extra.as_ref(), ); - */ apply_optional_value( artifacts_table, diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 0f5d8bea4..77756057d 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -48,23 +48,21 @@ fn apply_ci_github(ci_table: &mut toml_edit::Table, github: &GithubCiLayer) { apply_ci_common(gh_table, &github.common); - // FIXME(migration): Like with the v0 config, [dist.github.runners] and - // [dist.github.permissions] are not currently reformatted due to complexity. - /* - apply_optional_value( + // [dist.ci.github.runners] is not reformatted due to complexity. + skip_optional_value( gh_table, "runners", "# Custom GitHub runners, specified as target triples\n", - github.runners, + github.runners.as_ref(), ); - apply_optional_value( + // [dist.ci.github.permissions] is not reformatted due to complexity. + skip_optional_value( gh_table, "permissions", "# Custom permissions for jobs\n", - github.permissions, + github.permissions.as_ref(), ); - */ apply_optional_value( gh_table, diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs index f114e3b84..57c34b3bb 100644 --- a/cargo-dist/src/init/apply_dist/installers.rs +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -160,8 +160,13 @@ fn apply_installers_common(table: &mut toml_edit::Table, common: &CommonInstalle common.install_libraries.as_ref(), ); - // / Aliases to install binaries as - // TODO(migration): handle `pub bin_aliases: Option>>` + // [dist.ci.installers.bin-aliases] is not reformatted due to complexity. + skip_string_list( + table, + "bin-aliases", + "# Aliases to install for generated binaries\n", + common.bin_aliases.as_ref(), + ); } fn apply_installers_homebrew( From 3e59768a5298e9d6401fd013db0a3e6940a26bc4 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Tue, 11 Feb 2025 12:50:17 -0500 Subject: [PATCH 16/26] Comment out currently-unused use statements that I will likely need later. --- cargo-dist/src/init/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cargo-dist/src/init/mod.rs b/cargo-dist/src/init/mod.rs index b8324e992..fda5d4352 100644 --- a/cargo-dist/src/init/mod.rs +++ b/cargo-dist/src/init/mod.rs @@ -5,11 +5,13 @@ pub mod console_helpers; mod dist_profile; mod init_args; +/* use crate::config::{self, v1::TomlLayer, Config}; use crate::errors::DistResult; use crate::migrate; use crate::SortedMap; use crate::{do_generate, GenerateArgs}; use console_helpers::theme; +*/ pub use dist_profile::init_dist_profile; pub use init_args::InitArgs; From 41601ce16aaa9ea321f8b33182907dff00dc46a6 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Tue, 11 Feb 2025 12:52:32 -0500 Subject: [PATCH 17/26] Fix cargo fmt warnings. --- cargo-dist/src/init/apply_dist/artifacts.rs | 2 +- cargo-dist/src/init/apply_dist/installers.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/artifacts.rs b/cargo-dist/src/init/apply_dist/artifacts.rs index 04efd3105..146285376 100644 --- a/cargo-dist/src/init/apply_dist/artifacts.rs +++ b/cargo-dist/src/init/apply_dist/artifacts.rs @@ -96,8 +96,8 @@ fn apply_artifacts_archives( #[cfg(test)] mod test { use super::*; - use crate::{CompressionImpl, ChecksumStyle, ZipStyle}; use crate::config::LibraryStyle; + use crate::{ChecksumStyle, CompressionImpl, ZipStyle}; use axoasset::toml_edit::{self, DocumentMut, Item, Table}; use miette::IntoDiagnostic; use pretty_assertions::{assert_eq, assert_ne}; diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs index 57c34b3bb..a27ac865e 100644 --- a/cargo-dist/src/init/apply_dist/installers.rs +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -310,9 +310,9 @@ fn apply_installers_pkg(installers_table: &mut toml_edit::Table, pkg: &PkgInstal #[cfg(test)] mod test { use super::*; - use crate::{CompressionImpl, ChecksumStyle, ZipStyle}; use crate::config::LibraryStyle; use crate::init::apply_dist::InstallPathStrategy; + use crate::{ChecksumStyle, CompressionImpl, ZipStyle}; use miette::IntoDiagnostic; use pretty_assertions::{assert_eq, assert_ne}; @@ -426,7 +426,6 @@ always-use-latest-updater = true assert_eq!(expected, toml_text); } - #[test] fn apply_installers_complex() { let expected = r#" @@ -471,7 +470,6 @@ identifier = "pkg-identifier" install-location = "pkg-install-location" "#; - let installers = Some(InstallerLayer { common: CommonInstallerLayer { install_path: Some(vec![ From 664cb19caa878d9c2f097bb41dfac885484dda83 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 16:06:10 -0500 Subject: [PATCH 18/26] Move use statement to avoid warning. --- cargo-dist/src/init/apply_dist/ci.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 77756057d..3dc3df10a 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -2,7 +2,7 @@ use super::helpers::*; use crate::config::v1::ci::github::GithubCiLayer; use crate::config::v1::ci::{CiLayer, CommonCiLayer}; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit::{self, DocumentMut, Item, Table}; +use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, ci: &Option) { let Some(ci) = ci else { @@ -182,6 +182,7 @@ fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { mod test { use super::*; use crate::config::JobStyle; + use axoasset::toml_edit::DocumentMut; use miette::IntoDiagnostic; fn source() -> toml_edit::DocumentMut { From 4115685e0673981e28556c22e557845fd66dc581 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 16:06:23 -0500 Subject: [PATCH 19/26] Finish apply_dist::builds::apply(). --- cargo-dist/src/init/apply_dist/builds.rs | 221 ++++++++++++++++++++--- 1 file changed, 199 insertions(+), 22 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs index 0c03b0d6c..bf4318b9e 100644 --- a/cargo-dist/src/init/apply_dist/builds.rs +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -2,25 +2,24 @@ use super::helpers::*; use super::system_dependencies; use crate::config::v1::builds::BuildLayer; use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit; +use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, builds: &Option) { let Some(builds) = builds else { // Nothing to do. return; }; - let Some(builds_table) = table.get_mut("builds") else { - // Nothing to do. - return; - }; - let toml_edit::Item::Table(builds_table) = builds_table else { - panic!("Expected [dist.builds] to be a table"); - }; + + let builds_table = table + .entry("builds") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.builds] should be a table"); apply_optional_value( builds_table, "ssldotcom-windows-sign", - "# Whether we should sign Windows binaries using ssl.com", + "# Whether we should sign Windows binaries using ssl.com\n", builds .ssldotcom_windows_sign .as_ref() @@ -37,13 +36,6 @@ pub fn apply(table: &mut toml_edit::Table, builds: &Option) { apply_cargo_builds(builds_table, builds); system_dependencies::apply(builds_table, builds.system_dependencies.as_ref()); - apply_optional_min_glibc_version( - builds_table, - "min-glibc-version", - "# The minimum glibc version supported by the package (overrides auto-detection)\n", - builds.min_glibc_version.as_ref(), - ); - apply_optional_value( builds_table, "omnibor", @@ -51,6 +43,13 @@ pub fn apply(table: &mut toml_edit::Table, builds: &Option) { builds.omnibor, ); + apply_optional_min_glibc_version( + builds_table, + "min-glibc-version", + "\n# The minimum glibc version supported by the package (overrides auto-detection)\n", + builds.min_glibc_version.as_ref(), + ); + // Finalize the table builds_table .decor_mut() @@ -72,12 +71,11 @@ fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) return; }; - let mut possible_table = toml_edit::table(); - let cargo_builds_table = builds_table.get_mut("cargo").unwrap_or(&mut possible_table); - - let toml_edit::Item::Table(cargo_builds_table) = cargo_builds_table else { - panic!("Expected [dist.builds.cargo] to be a table") - }; + let cargo_builds_table = builds_table + .entry("cargo") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.builds.cargo] should be a bool or a table"); apply_optional_value( cargo_builds_table, @@ -140,3 +138,182 @@ fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) .decor_mut() .set_prefix("\n# How dist should build Cargo projects\n"); } + +#[cfg(test)] +mod test { + use super::*; + use miette::IntoDiagnostic; + use axoasset::toml_edit::DocumentMut; + use crate::config::v1::builds::cargo::CargoBuildLayer; + use crate::config::v1::builds::generic::GenericBuildLayer; + use crate::config::v1::builds::CommonBuildLayer; + use crate::config::{ProductionMode, SystemDependencies}; + use pretty_assertions::{assert_eq, assert_ne}; + + fn source() -> toml_edit::DocumentMut { + let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); + let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); + doc + } + + // Given a DocumentMut, make sure it has a [dist] table, and return + // a reference to that dist table. + fn dist_table(doc: &mut toml_edit::DocumentMut) -> &mut toml_edit::Table { + let dist = doc + .entry("dist") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .unwrap(); + // Don't show the empty top-level [dist]. + dist.set_implicit(true); + // Return the table we just created. + dist + } + + #[test] + fn apply_empty() { + let expected = ""; + + let layer = Some(BuildLayer { + common: CommonBuildLayer {}, + ssldotcom_windows_sign: None, + macos_sign: None, + cargo: None, + generic: None, + system_dependencies: None, + min_glibc_version: None, + omnibor: None, + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = table.to_string(); + assert_eq!(toml_text, expected); + } + + #[test] + fn apply_everything() { + let expected = r#" +# Build configuration for dist +[dist.builds] +# Whether we should sign Windows binaries using ssl.com +ssldotcom-windows-sign = "test" +# Whether to sign macOS executables +macos-sign = true +# Whether dist should build cargo projects +# (Use the table format of [dist.builds.cargo] for more nuanced config!) +cargo = true +# Whether to use omnibor-cli to generate OmniBOR Artifact IDs +omnibor = true + +# The minimum glibc version supported by the package (overrides auto-detection) +[dist.builds.min-glibc-version] +some-target = "1.2" +"#; + + let mut min_glibc = crate::platform::MinGlibcVersion::new(); + min_glibc.insert("some-target".to_string(), crate::platform::LibcVersion { + major: 1, + series: 2, + }); + + let layer = Some(BuildLayer { + common: CommonBuildLayer {}, + ssldotcom_windows_sign: Some(ProductionMode::Test), + macos_sign: Some(true), + cargo: Some(BoolOr::Bool(true)), + generic: Some(BoolOr::Bool(true)), + system_dependencies: None, + min_glibc_version: Some(min_glibc), + omnibor: Some(true), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } + + #[test] + fn apply_complex() { + let expected = r#" +# Build configuration for dist +[dist.builds] +# Whether we should sign Windows binaries using ssl.com +ssldotcom-windows-sign = "test" +# Whether to sign macOS executables +macos-sign = true +# Whether to use omnibor-cli to generate OmniBOR Artifact IDs +omnibor = true + +# How dist should build Cargo projects +[dist.builds.cargo] +# Whether +crt-static should be used on msvc +msvc-crt-static = true +# Build only the required packages, and individually +precise-builds = true +# Features to pass to cargo build +features = ["some-feature"] +# Whether default-features should be enabled with cargo build +default-features = true +# Whether to pass --all-features to cargo build +all-features = true +# Whether to embed dependency information using cargo-auditable +cargo-auditable = true +# Whether to use cargo-cyclonedx to generate an SBOM +cargo-cyclonedx = true + +# The minimum glibc version supported by the package (overrides auto-detection) +[dist.builds.min-glibc-version] +some-target = "1.2" +"#; + + let mut min_glibc = crate::platform::MinGlibcVersion::new(); + min_glibc.insert("some-target".to_string(), crate::platform::LibcVersion { + major: 1, + series: 2, + }); + + let cargo_bl = CargoBuildLayer { + common: CommonBuildLayer {}, + // Deprecated/v0-specific. + rust_toolchain_version: None, + msvc_crt_static: Some(true), + precise_builds: Some(true), + features: Some(vec!["some-feature".to_string()]), + default_features: Some(true), + all_features: Some(true), + cargo_auditable: Some(true), + cargo_cyclonedx: Some(true), + }; + + let generic_bl = GenericBuildLayer { + common: CommonBuildLayer {}, + }; + + let layer = Some(BuildLayer { + common: CommonBuildLayer {}, + ssldotcom_windows_sign: Some(ProductionMode::Test), + macos_sign: Some(true), + cargo: Some(BoolOr::Val(cargo_bl)), + generic: Some(BoolOr::Val(generic_bl)), + system_dependencies: None, + min_glibc_version: Some(min_glibc), + omnibor: Some(true), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } +} From 6604dc92596a6505d4d833b6237b3c84d26be1c2 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 16:43:57 -0500 Subject: [PATCH 20/26] Finish apply_dist::publishers::apply() --- cargo-dist/src/init/apply_dist/publishers.rs | 217 ++++++++++++++++++- 1 file changed, 209 insertions(+), 8 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/publishers.rs b/cargo-dist/src/init/apply_dist/publishers.rs index 4b4040371..554dffb34 100644 --- a/cargo-dist/src/init/apply_dist/publishers.rs +++ b/cargo-dist/src/init/apply_dist/publishers.rs @@ -1,20 +1,221 @@ use super::helpers::*; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use crate::config::v1::publishers::PublisherLayer; -use axoasset::toml_edit; +use crate::config::v1::layer::BoolOr; +use crate::config::v1::publishers::{CommonPublisherLayer, PublisherLayer}; +use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, publishers: &Option) { - let Some(publishers_table) = table.get_mut("publishers") else { + let Some(publishers) = publishers else { + // Nothing to do. return; }; - let toml_edit::Item::Table(publishers_table) = publishers_table else { - panic!("Expected [dist.publishers] to be a table"); - }; - // TODO(migration): implement this + let publishers_table = table + .entry("publishers") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.publishers] should be a table"); + + + apply_common(publishers_table, &publishers.common); + apply_homebrew(publishers_table, &publishers); + apply_npm(publishers_table, &publishers); // Finalize the table publishers_table .decor_mut() .set_prefix("\n# Publisher configuration for dist\n"); } + +fn apply_common(table: &mut toml_edit::Table, common: &CommonPublisherLayer) { + apply_optional_value( + table, + "prereleases", + "# Whether to publish prereleases (defaults to false)\n", + common.prereleases, + ); +} + +fn apply_homebrew(publishers_table: &mut toml_edit::Table, publishers: &PublisherLayer) { + if let Some(BoolOr::Bool(b)) = publishers.homebrew { + // If it was set as a boolean, simply set it as a boolean and return. + apply_optional_value(publishers_table, + "homebrew", + "# Whether to publish to Homebrew\n", + Some(b), + ); + return; + } + + let Some(BoolOr::Val(ref homebrew)) = publishers.homebrew else { + // dist.publishers.homebrew isn't specified; nothing to do. + return; + }; + + let hb_table = publishers_table + .entry("homebrew") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.publishers.homebrew] should be a bool or a table"); + + apply_common(hb_table, &homebrew.common); + + // Finalize the table + publishers_table + .decor_mut() + .set_prefix("\n# Configuration for publishing to Homebrew\n"); +} + +fn apply_npm(publishers_table: &mut toml_edit::Table, publishers: &PublisherLayer) { + if let Some(BoolOr::Bool(b)) = publishers.npm { + // If it was set as a boolean, simply set it as a boolean and return. + apply_optional_value(publishers_table, + "npm", + "# Whether to publish to NPM\n", + Some(b), + ); + return; + } + + let Some(BoolOr::Val(ref npm)) = publishers.npm else { + // dist.publishers.npm isn't specified; nothing to do. + return; + }; + + let hb_table = publishers_table + .entry("npm") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.publishers.npm] should be a bool or a table"); + + apply_common(hb_table, &npm.common); + + // Finalize the table + publishers_table + .decor_mut() + .set_prefix("\n# Configuration for publishing to NPM\n"); +} + + + +#[cfg(test)] +mod test { + use super::*; + use crate::config::v1::publishers::homebrew::HomebrewPublisherLayer; + use crate::config::v1::publishers::npm::NpmPublisherLayer; + use miette::IntoDiagnostic; + use axoasset::toml_edit::DocumentMut; + use pretty_assertions::{assert_eq, assert_ne}; + + fn source() -> toml_edit::DocumentMut { + let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); + let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); + doc + } + + // Given a DocumentMut, make sure it has a [dist] table, and return + // a reference to that dist table. + fn dist_table(doc: &mut toml_edit::DocumentMut) -> &mut toml_edit::Table { + let dist = doc + .entry("dist") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .unwrap(); + // Don't show the empty top-level [dist]. + dist.set_implicit(true); + // Return the table we just created. + dist + } + + #[test] + fn apply_empty() { + let expected = ""; + + let layer = Some(PublisherLayer { + common: CommonPublisherLayer { + prereleases: None, + }, + homebrew: None, + npm: None, + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = table.to_string(); + assert_eq!(toml_text, expected); + } + + #[test] + fn apply_everything() { + let expected = r#" +# Publisher configuration for dist +[dist.publishers] +# Whether to publish prereleases (defaults to false) +prereleases = true +# Whether to publish to Homebrew +homebrew = true +# Whether to publish to NPM +npm = true +"#; + + let layer = Some(PublisherLayer { + common: CommonPublisherLayer { + prereleases: Some(true), + }, + homebrew: Some(BoolOr::Bool(true)), + npm: Some(BoolOr::Bool(true)), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } + + #[test] + fn apply_complex() { + let expected = r#" +# Publisher configuration for dist +[dist.publishers] +# Whether to publish prereleases (defaults to false) +prereleases = true + +[dist.publishers.homebrew] +# Whether to publish prereleases (defaults to false) +prereleases = true + +[dist.publishers.npm] +# Whether to publish prereleases (defaults to false) +prereleases = true +"#; + + let layer = Some(PublisherLayer { + common: CommonPublisherLayer { + prereleases: Some(true), + }, + homebrew: Some(BoolOr::Val(HomebrewPublisherLayer { + common: CommonPublisherLayer { + prereleases: Some(true), + }, + })), + npm: Some(BoolOr::Val(NpmPublisherLayer { + common: CommonPublisherLayer { + prereleases: Some(true), + }, + })), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } +} From 4278de8f26be842449f5af0ce319d63fc00b383e Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 17:00:38 -0500 Subject: [PATCH 21/26] Fix a bunch of clippy warnings. --- cargo-dist/src/init/apply_dist/artifacts.rs | 9 +++------ cargo-dist/src/init/apply_dist/builds.rs | 8 +++----- cargo-dist/src/init/apply_dist/ci.rs | 7 ++----- cargo-dist/src/init/apply_dist/helpers.rs | 2 +- cargo-dist/src/init/apply_dist/installers.rs | 10 ++++------ cargo-dist/src/init/apply_dist/mod.rs | 2 +- cargo-dist/src/init/apply_dist/publishers.rs | 10 ++++------ 7 files changed, 18 insertions(+), 30 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/artifacts.rs b/cargo-dist/src/init/apply_dist/artifacts.rs index 146285376..72a6c0c49 100644 --- a/cargo-dist/src/init/apply_dist/artifacts.rs +++ b/cargo-dist/src/init/apply_dist/artifacts.rs @@ -1,8 +1,7 @@ use super::helpers::*; use crate::config::v1::artifacts::archives::ArchiveLayer; use crate::config::v1::artifacts::ArtifactLayer; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit::{self, DocumentMut, Item, Table}; +use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, artifacts: &Option) { let Some(artifacts) = artifacts else { @@ -98,14 +97,12 @@ mod test { use super::*; use crate::config::LibraryStyle; use crate::{ChecksumStyle, CompressionImpl, ZipStyle}; - use axoasset::toml_edit::{self, DocumentMut, Item, Table}; use miette::IntoDiagnostic; - use pretty_assertions::{assert_eq, assert_ne}; + use pretty_assertions::assert_eq; fn source() -> toml_edit::DocumentMut { let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); - let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); - doc + src.deserialize_toml_edit().into_diagnostic().unwrap() } // Given a DocumentMut, make sure it has a [dist] table, and return diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs index bf4318b9e..061cbab4d 100644 --- a/cargo-dist/src/init/apply_dist/builds.rs +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -1,7 +1,7 @@ use super::helpers::*; use super::system_dependencies; use crate::config::v1::builds::BuildLayer; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::v1::layer::BoolOr; use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, builds: &Option) { @@ -143,17 +143,15 @@ fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) mod test { use super::*; use miette::IntoDiagnostic; - use axoasset::toml_edit::DocumentMut; use crate::config::v1::builds::cargo::CargoBuildLayer; use crate::config::v1::builds::generic::GenericBuildLayer; use crate::config::v1::builds::CommonBuildLayer; use crate::config::{ProductionMode, SystemDependencies}; - use pretty_assertions::{assert_eq, assert_ne}; + use pretty_assertions::assert_eq; fn source() -> toml_edit::DocumentMut { let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); - let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); - doc + src.deserialize_toml_edit().into_diagnostic().unwrap() } // Given a DocumentMut, make sure it has a [dist] table, and return diff --git a/cargo-dist/src/init/apply_dist/ci.rs b/cargo-dist/src/init/apply_dist/ci.rs index 3dc3df10a..35d0d6fe1 100644 --- a/cargo-dist/src/init/apply_dist/ci.rs +++ b/cargo-dist/src/init/apply_dist/ci.rs @@ -1,7 +1,7 @@ use super::helpers::*; use crate::config::v1::ci::github::GithubCiLayer; use crate::config::v1::ci::{CiLayer, CommonCiLayer}; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::v1::layer::BoolOr; use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, ci: &Option) { @@ -181,14 +181,11 @@ fn apply_ci_common(table: &mut toml_edit::Table, common: &CommonCiLayer) { #[cfg(test)] mod test { use super::*; - use crate::config::JobStyle; - use axoasset::toml_edit::DocumentMut; use miette::IntoDiagnostic; fn source() -> toml_edit::DocumentMut { let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); - let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); - doc + src.deserialize_toml_edit().into_diagnostic().unwrap() } // Given a DocumentMut, make sure it has a [dist] table, and return diff --git a/cargo-dist/src/init/apply_dist/helpers.rs b/cargo-dist/src/init/apply_dist/helpers.rs index c9a697d34..082912600 100644 --- a/cargo-dist/src/init/apply_dist/helpers.rs +++ b/cargo-dist/src/init/apply_dist/helpers.rs @@ -11,7 +11,7 @@ pub fn skip_optional_value( trace!("apply_dist/skipping: {}", key); } -pub fn skip_string_list(_table: &mut toml_edit::Table, key: &str, desc: &str, _list: Option) { +pub fn skip_string_list(_table: &mut toml_edit::Table, key: &str, _desc: &str, _list: Option) { trace!("apply_dist/skipping: {}", key); } diff --git a/cargo-dist/src/init/apply_dist/installers.rs b/cargo-dist/src/init/apply_dist/installers.rs index a27ac865e..8b8a6b068 100644 --- a/cargo-dist/src/init/apply_dist/installers.rs +++ b/cargo-dist/src/init/apply_dist/installers.rs @@ -1,6 +1,6 @@ use super::helpers::*; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit::{self, DocumentMut, Item, Table}; +use crate::config::v1::layer::BoolOr; +use axoasset::toml_edit::{self, Item, Table}; use crate::config::v1::installers::{ homebrew::HomebrewInstallerLayer, msi::MsiInstallerLayer, npm::NpmInstallerLayer, @@ -312,14 +312,12 @@ mod test { use super::*; use crate::config::LibraryStyle; use crate::init::apply_dist::InstallPathStrategy; - use crate::{ChecksumStyle, CompressionImpl, ZipStyle}; use miette::IntoDiagnostic; - use pretty_assertions::{assert_eq, assert_ne}; + use pretty_assertions::assert_eq; fn source() -> toml_edit::DocumentMut { let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); - let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); - doc + src.deserialize_toml_edit().into_diagnostic().unwrap() } // Given a DocumentMut, make sure it has a [dist] table, and return diff --git a/cargo-dist/src/init/apply_dist/mod.rs b/cargo-dist/src/init/apply_dist/mod.rs index 02512ef92..ee4829757 100644 --- a/cargo-dist/src/init/apply_dist/mod.rs +++ b/cargo-dist/src/init/apply_dist/mod.rs @@ -1,5 +1,5 @@ use crate::config::v1::installers::InstallerLayer; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; +use crate::config::v1::layer::BoolOrOptExt; use crate::config::v1::TomlLayer; use crate::config::InstallPathStrategy; use crate::METADATA_DIST; diff --git a/cargo-dist/src/init/apply_dist/publishers.rs b/cargo-dist/src/init/apply_dist/publishers.rs index 554dffb34..6bddf63bb 100644 --- a/cargo-dist/src/init/apply_dist/publishers.rs +++ b/cargo-dist/src/init/apply_dist/publishers.rs @@ -17,8 +17,8 @@ pub fn apply(table: &mut toml_edit::Table, publishers: &Option) apply_common(publishers_table, &publishers.common); - apply_homebrew(publishers_table, &publishers); - apply_npm(publishers_table, &publishers); + apply_homebrew(publishers_table, publishers); + apply_npm(publishers_table, publishers); // Finalize the table publishers_table @@ -103,13 +103,11 @@ mod test { use crate::config::v1::publishers::homebrew::HomebrewPublisherLayer; use crate::config::v1::publishers::npm::NpmPublisherLayer; use miette::IntoDiagnostic; - use axoasset::toml_edit::DocumentMut; - use pretty_assertions::{assert_eq, assert_ne}; + use pretty_assertions::assert_eq; fn source() -> toml_edit::DocumentMut { let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); - let doc = src.deserialize_toml_edit().into_diagnostic().unwrap(); - doc + src.deserialize_toml_edit().into_diagnostic().unwrap() } // Given a DocumentMut, make sure it has a [dist] table, and return From f7d3066d2d551ac100f7b3ca57d44b5f0108f528 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 17:03:36 -0500 Subject: [PATCH 22/26] Fix cargo-fmt warnings. --- cargo-dist/src/init/apply_dist/builds.rs | 28 ++++++++++++-------- cargo-dist/src/init/apply_dist/helpers.rs | 7 ++++- cargo-dist/src/init/apply_dist/publishers.rs | 13 ++++----- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs index 061cbab4d..d487a2cbc 100644 --- a/cargo-dist/src/init/apply_dist/builds.rs +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -142,11 +142,11 @@ fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) #[cfg(test)] mod test { use super::*; - use miette::IntoDiagnostic; use crate::config::v1::builds::cargo::CargoBuildLayer; use crate::config::v1::builds::generic::GenericBuildLayer; use crate::config::v1::builds::CommonBuildLayer; use crate::config::{ProductionMode, SystemDependencies}; + use miette::IntoDiagnostic; use pretty_assertions::assert_eq; fn source() -> toml_edit::DocumentMut { @@ -213,12 +213,15 @@ some-target = "1.2" "#; let mut min_glibc = crate::platform::MinGlibcVersion::new(); - min_glibc.insert("some-target".to_string(), crate::platform::LibcVersion { - major: 1, - series: 2, - }); + min_glibc.insert( + "some-target".to_string(), + crate::platform::LibcVersion { + major: 1, + series: 2, + }, + ); - let layer = Some(BuildLayer { + let layer = Some(BuildLayer { common: CommonBuildLayer {}, ssldotcom_windows_sign: Some(ProductionMode::Test), macos_sign: Some(true), @@ -273,10 +276,13 @@ some-target = "1.2" "#; let mut min_glibc = crate::platform::MinGlibcVersion::new(); - min_glibc.insert("some-target".to_string(), crate::platform::LibcVersion { - major: 1, - series: 2, - }); + min_glibc.insert( + "some-target".to_string(), + crate::platform::LibcVersion { + major: 1, + series: 2, + }, + ); let cargo_bl = CargoBuildLayer { common: CommonBuildLayer {}, @@ -295,7 +301,7 @@ some-target = "1.2" common: CommonBuildLayer {}, }; - let layer = Some(BuildLayer { + let layer = Some(BuildLayer { common: CommonBuildLayer {}, ssldotcom_windows_sign: Some(ProductionMode::Test), macos_sign: Some(true), diff --git a/cargo-dist/src/init/apply_dist/helpers.rs b/cargo-dist/src/init/apply_dist/helpers.rs index 082912600..5dae1c346 100644 --- a/cargo-dist/src/init/apply_dist/helpers.rs +++ b/cargo-dist/src/init/apply_dist/helpers.rs @@ -11,7 +11,12 @@ pub fn skip_optional_value( trace!("apply_dist/skipping: {}", key); } -pub fn skip_string_list(_table: &mut toml_edit::Table, key: &str, _desc: &str, _list: Option) { +pub fn skip_string_list( + _table: &mut toml_edit::Table, + key: &str, + _desc: &str, + _list: Option, +) { trace!("apply_dist/skipping: {}", key); } diff --git a/cargo-dist/src/init/apply_dist/publishers.rs b/cargo-dist/src/init/apply_dist/publishers.rs index 6bddf63bb..48ed34c7b 100644 --- a/cargo-dist/src/init/apply_dist/publishers.rs +++ b/cargo-dist/src/init/apply_dist/publishers.rs @@ -15,7 +15,6 @@ pub fn apply(table: &mut toml_edit::Table, publishers: &Option) .as_table_mut() .expect("[dist.publishers] should be a table"); - apply_common(publishers_table, &publishers.common); apply_homebrew(publishers_table, publishers); apply_npm(publishers_table, publishers); @@ -38,7 +37,8 @@ fn apply_common(table: &mut toml_edit::Table, common: &CommonPublisherLayer) { fn apply_homebrew(publishers_table: &mut toml_edit::Table, publishers: &PublisherLayer) { if let Some(BoolOr::Bool(b)) = publishers.homebrew { // If it was set as a boolean, simply set it as a boolean and return. - apply_optional_value(publishers_table, + apply_optional_value( + publishers_table, "homebrew", "# Whether to publish to Homebrew\n", Some(b), @@ -68,7 +68,8 @@ fn apply_homebrew(publishers_table: &mut toml_edit::Table, publishers: &Publishe fn apply_npm(publishers_table: &mut toml_edit::Table, publishers: &PublisherLayer) { if let Some(BoolOr::Bool(b)) = publishers.npm { // If it was set as a boolean, simply set it as a boolean and return. - apply_optional_value(publishers_table, + apply_optional_value( + publishers_table, "npm", "# Whether to publish to NPM\n", Some(b), @@ -95,8 +96,6 @@ fn apply_npm(publishers_table: &mut toml_edit::Table, publishers: &PublisherLaye .set_prefix("\n# Configuration for publishing to NPM\n"); } - - #[cfg(test)] mod test { use super::*; @@ -129,9 +128,7 @@ mod test { let expected = ""; let layer = Some(PublisherLayer { - common: CommonPublisherLayer { - prereleases: None, - }, + common: CommonPublisherLayer { prereleases: None }, homebrew: None, npm: None, }); From 2a805ff69d3a5d31f6372ea14947a9fc2ca1a816 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 17:08:48 -0500 Subject: [PATCH 23/26] Integrate apply_dist::system_dependencies into apply_dist::build --- cargo-dist/src/init/apply_dist/builds.rs | 11 +++++++++-- cargo-dist/src/init/apply_dist/mod.rs | 1 - .../src/init/apply_dist/system_dependencies.rs | 16 ---------------- 3 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 cargo-dist/src/init/apply_dist/system_dependencies.rs diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs index d487a2cbc..2a2a70206 100644 --- a/cargo-dist/src/init/apply_dist/builds.rs +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -1,7 +1,7 @@ use super::helpers::*; -use super::system_dependencies; use crate::config::v1::builds::BuildLayer; use crate::config::v1::layer::BoolOr; +use crate::config::SystemDependencies; use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, builds: &Option) { @@ -34,7 +34,7 @@ pub fn apply(table: &mut toml_edit::Table, builds: &Option) { ); apply_cargo_builds(builds_table, builds); - system_dependencies::apply(builds_table, builds.system_dependencies.as_ref()); + apply_system_dependencies(builds_table, builds.system_dependencies.as_ref()); apply_optional_value( builds_table, @@ -56,6 +56,13 @@ pub fn apply(table: &mut toml_edit::Table, builds: &Option) { .set_prefix("\n# Build configuration for dist\n"); } +pub fn apply_system_dependencies( + _builds_table: &mut toml_edit::Table, + _system_dependencies: Option<&SystemDependencies>, +) { + // This is complex enough we don't support editing it in init, so this does nothing. +} + fn apply_cargo_builds(builds_table: &mut toml_edit::Table, builds: &BuildLayer) { if let Some(BoolOr::Bool(b)) = builds.cargo { // If it was set as a boolean, simply set it as a boolean and return. diff --git a/cargo-dist/src/init/apply_dist/mod.rs b/cargo-dist/src/init/apply_dist/mod.rs index ee4829757..5ef8597a1 100644 --- a/cargo-dist/src/init/apply_dist/mod.rs +++ b/cargo-dist/src/init/apply_dist/mod.rs @@ -12,7 +12,6 @@ mod helpers; mod hosts; mod installers; mod publishers; -mod system_dependencies; use helpers::*; diff --git a/cargo-dist/src/init/apply_dist/system_dependencies.rs b/cargo-dist/src/init/apply_dist/system_dependencies.rs deleted file mode 100644 index 84f7687d5..000000000 --- a/cargo-dist/src/init/apply_dist/system_dependencies.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::helpers::*; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use crate::config::SystemDependencies; -use axoasset::toml_edit; - -pub fn apply( - builds_table: &mut toml_edit::Table, - system_dependencies: Option<&SystemDependencies>, -) { - let Some(system_dependencies) = system_dependencies else { - // Nothing to do. - return; - }; - - // TODO(migration): implement this -} From a2ceec1d76f07fe58bea5bbc527f529399b9cf73 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 17:48:46 -0500 Subject: [PATCH 24/26] Finish apply_dist::hosts --- cargo-dist/src/init/apply_dist/hosts.rs | 280 +++++++++++++++++++++++- 1 file changed, 273 insertions(+), 7 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/hosts.rs b/cargo-dist/src/init/apply_dist/hosts.rs index a44277eb4..ac3cf0ff8 100644 --- a/cargo-dist/src/init/apply_dist/hosts.rs +++ b/cargo-dist/src/init/apply_dist/hosts.rs @@ -1,21 +1,287 @@ use super::helpers::*; use crate::config::v1::hosts::HostLayer; -use crate::config::v1::layer::{BoolOr, BoolOrOptExt}; -use axoasset::toml_edit; +use crate::config::v1::layer::BoolOr; +use axoasset::toml_edit::{self, Item, Table}; pub fn apply(table: &mut toml_edit::Table, hosts: &Option) { - let Some(hosts_table) = table.get_mut("hosts") else { + let Some(hosts) = hosts else { // Nothing to do. return; }; - let toml_edit::Item::Table(hosts_table) = hosts_table else { - panic!("Expected [dist.hosts] to be a table"); - }; - // TODO(migration): implement this + let hosts_table = table + .entry("hosts") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.hosts] should be a table"); + + // hosts.common is just `CommonHostLayer {}`, so there's nothing to do. + + apply_optional_value( + hosts_table, + "force-latest", + "# Always regard releases as stable (defaults to false)\n", + hosts.force_latest, + ); + + apply_optional_value( + hosts_table, + "display", + "# Whether artifacts/installers for this app should be displayed in release bodies\n", + hosts.display, + ); + + apply_optional_value( + hosts_table, + "display-name", + "# How to refer to the app in release bodies\n", + hosts.display_name.as_ref(), + ); + + apply_github(hosts_table, hosts); + apply_axodotdev(hosts_table, hosts); // Finalize the table hosts_table .decor_mut() .set_prefix("\n# Hosting configuration for dist\n"); } + +fn apply_github(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { + if let Some(BoolOr::Bool(b)) = hosts.github { + // If it was set as a boolean, simply set it as a boolean and return. + apply_optional_value(hosts_table, + "github", + "# Configuration for GitHub hosting\n# (Use the table format of [dist.hosts.github] for more nuanced config!)\n", + Some(b), + ); + return; + } + + let Some(BoolOr::Val(ref github)) = hosts.github else { + return; + }; + + let gh_table = hosts_table + .entry("github") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .expect("[dist.hosts.github] should be a bool or a table"); + + apply_optional_value( + gh_table, + "create", + "# Whether dist should create the GitHub release (default: true)\n", + github.create, + ); + + apply_optional_value( + gh_table, + "repo", + "# Publish GitHub Releases to this repo instead\n", + github.repo.as_ref().map(|a| a.to_string()), + ); + + apply_optional_value( + gh_table, + "during", + "# Which phase dist should use to create the GitHub release\n", + github.during.as_ref().map(|a| a.to_string()), + ); + + apply_optional_value( + gh_table, + "submodule-path", + "# Read the commit to be tagged from the submodule at this path\n", + github.submodule_path + .as_ref() + .map(|a| a.to_string()), + ); + + apply_optional_value( + gh_table, + "attestations", + "# Whether to enable GitHub Attestations\n", + github.attestations, + ); + + // Finalize the table + gh_table + .decor_mut() + .set_prefix("\n# Configuration for GitHub hosting\n"); +} + +fn apply_axodotdev(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { + if let Some(BoolOr::Bool(b)) = hosts.axodotdev { + // If it was set as a boolean, simply set it as a boolean and return. + apply_optional_value(hosts_table, + "axodotdev", + "# Whether to use axo.dev hosting\n", + Some(b), + ); + return; + } + + let Some(BoolOr::Val(ref _axo)) = hosts.axodotdev else { + return; + }; + + // There is no reason for this to ever involve a struct, + // but it does and I don't have time to untangle it. + // + // So: If the table exists, we turn it into `axodotdev=true`. + // + // Theoretically, there's no valid representation of AxodotdevHostLayer + // which isn't empty, so this should never run. + // -@duckinator + apply_optional_value(hosts_table, + "axodotdev", + "# Whether to use axo.dev hosting\n", + Some(true), + ); +} + +#[cfg(test)] +mod test { + use super::*; + use crate::config::v1::hosts::CommonHostLayer; + use crate::config::v1::hosts::github::GithubHostLayer; + use crate::config::{GithubReleasePhase, GithubRepoPair}; + use miette::IntoDiagnostic; + use pretty_assertions::assert_eq; + + fn source() -> toml_edit::DocumentMut { + let src = axoasset::SourceFile::new("fake-dist-workspace.toml", String::new()); + src.deserialize_toml_edit().into_diagnostic().unwrap() + } + + // Given a DocumentMut, make sure it has a [dist] table, and return + // a reference to that dist table. + fn dist_table(doc: &mut toml_edit::DocumentMut) -> &mut toml_edit::Table { + let dist = doc + .entry("dist") + .or_insert(Item::Table(Table::new())) + .as_table_mut() + .unwrap(); + // Don't show the empty top-level [dist]. + dist.set_implicit(true); + // Return the table we just created. + dist + } + + #[test] + fn apply_empty() { + let expected = ""; + + let layer = Some(HostLayer { + common: CommonHostLayer {}, + force_latest: None, + display: None, + display_name: None, + github: None, + axodotdev: None, + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = table.to_string(); + assert_eq!(toml_text, expected); + } + + #[test] + fn apply_everything() { + let expected = r#" +# Hosting configuration for dist +[dist.hosts] +# Always regard releases as stable (defaults to false) +force-latest = true +# Whether artifacts/installers for this app should be displayed in release bodies +display = true +# How to refer to the app in release bodies +display-name = "some-name" +# Configuration for GitHub hosting +# (Use the table format of [dist.hosts.github] for more nuanced config!) +github = true +# Whether to use axo.dev hosting +axodotdev = true +"#; + + let layer = Some(HostLayer { + common: CommonHostLayer {}, + force_latest: Some(true), + display: Some(true), + display_name: Some("some-name".to_string()), + github: Some(BoolOr::Bool(true)), + axodotdev: Some(BoolOr::Bool(true)), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } + + #[test] + fn apply_complex() { + let expected = r#" +# Hosting configuration for dist +[dist.hosts] +# Always regard releases as stable (defaults to false) +force-latest = true +# Whether artifacts/installers for this app should be displayed in release bodies +display = true +# How to refer to the app in release bodies +display-name = "some-name" +# Whether to use axo.dev hosting +axodotdev = true + +# Configuration for GitHub hosting +[dist.hosts.github] +# Whether dist should create the GitHub release (default: true) +create = true +# Publish GitHub Releases to this repo instead +repo = "example-user/example-repo" +# Which phase dist should use to create the GitHub release +during = "auto" +# Read the commit to be tagged from the submodule at this path +submodule-path = "./foo" +# Whether to enable GitHub Attestations +attestations = true +"#; + + let github = GithubHostLayer { + common: CommonHostLayer {}, + create: Some(true), + repo: Some(GithubRepoPair { + owner: "example-user".to_string(), + repo: "example-repo".to_string(), + }), + submodule_path: Some("./foo".into()), + during: Some(GithubReleasePhase::Auto), + attestations: Some(true), + }; + + let layer = Some(HostLayer { + common: CommonHostLayer {}, + force_latest: Some(true), + display: Some(true), + display_name: Some("some-name".to_string()), + github: Some(BoolOr::Val(github)), + axodotdev: Some(BoolOr::Bool(true)), + }); + + let mut doc = source(); + let table = dist_table(&mut doc); + + apply(table, &layer); + + let toml_text = doc.to_string(); + assert_eq!(expected, toml_text); + } +} From 290f2e074c2c3f4bedac63d431918ec5f02cf235 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 17:50:16 -0500 Subject: [PATCH 25/26] Fix clippy + cargo-fmt warnings. --- cargo-dist/src/init/apply_dist/builds.rs | 2 +- cargo-dist/src/init/apply_dist/hosts.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/builds.rs b/cargo-dist/src/init/apply_dist/builds.rs index 2a2a70206..79b6110ef 100644 --- a/cargo-dist/src/init/apply_dist/builds.rs +++ b/cargo-dist/src/init/apply_dist/builds.rs @@ -152,7 +152,7 @@ mod test { use crate::config::v1::builds::cargo::CargoBuildLayer; use crate::config::v1::builds::generic::GenericBuildLayer; use crate::config::v1::builds::CommonBuildLayer; - use crate::config::{ProductionMode, SystemDependencies}; + use crate::config::ProductionMode; use miette::IntoDiagnostic; use pretty_assertions::assert_eq; diff --git a/cargo-dist/src/init/apply_dist/hosts.rs b/cargo-dist/src/init/apply_dist/hosts.rs index ac3cf0ff8..22aa5acd4 100644 --- a/cargo-dist/src/init/apply_dist/hosts.rs +++ b/cargo-dist/src/init/apply_dist/hosts.rs @@ -93,9 +93,7 @@ fn apply_github(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { gh_table, "submodule-path", "# Read the commit to be tagged from the submodule at this path\n", - github.submodule_path - .as_ref() - .map(|a| a.to_string()), + github.submodule_path.as_ref().map(|a| a.to_string()), ); apply_optional_value( @@ -114,7 +112,8 @@ fn apply_github(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { fn apply_axodotdev(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { if let Some(BoolOr::Bool(b)) = hosts.axodotdev { // If it was set as a boolean, simply set it as a boolean and return. - apply_optional_value(hosts_table, + apply_optional_value( + hosts_table, "axodotdev", "# Whether to use axo.dev hosting\n", Some(b), @@ -134,7 +133,8 @@ fn apply_axodotdev(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { // Theoretically, there's no valid representation of AxodotdevHostLayer // which isn't empty, so this should never run. // -@duckinator - apply_optional_value(hosts_table, + apply_optional_value( + hosts_table, "axodotdev", "# Whether to use axo.dev hosting\n", Some(true), @@ -144,8 +144,8 @@ fn apply_axodotdev(hosts_table: &mut toml_edit::Table, hosts: &HostLayer) { #[cfg(test)] mod test { use super::*; - use crate::config::v1::hosts::CommonHostLayer; use crate::config::v1::hosts::github::GithubHostLayer; + use crate::config::v1::hosts::CommonHostLayer; use crate::config::{GithubReleasePhase, GithubRepoPair}; use miette::IntoDiagnostic; use pretty_assertions::assert_eq; From 5402a1fdb98fca06deb402d00f0660382f5b54d4 Mon Sep 17 00:00:00 2001 From: Ellen Marie Dash Date: Thu, 13 Feb 2025 17:50:58 -0500 Subject: [PATCH 26/26] Remove commented-out code, since it has all been replaced. --- cargo-dist/src/init/apply_dist/mod.rs | 128 -------------------------- 1 file changed, 128 deletions(-) diff --git a/cargo-dist/src/init/apply_dist/mod.rs b/cargo-dist/src/init/apply_dist/mod.rs index 5ef8597a1..b89d12a87 100644 --- a/cargo-dist/src/init/apply_dist/mod.rs +++ b/cargo-dist/src/init/apply_dist/mod.rs @@ -100,134 +100,6 @@ pub fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &TomlLayer) installers::apply(table, installers); publishers::apply(table, publishers); - // TODO(migration): make sure all of these are handled - /* - - apply_optional_value( - table, - "create-release", - "# Whether dist should create a Github Release or use an existing draft\n", - *create_release, - ); - - apply_optional_value( - table, - "github-release", - "# Which phase dist should use to create the GitHub release\n", - github_release.as_ref().map(|a| a.to_string()), - ); - - apply_optional_value( - table, - "github-releases-repo", - "# Publish GitHub Releases to this repo instead\n", - github_releases_repo.as_ref().map(|a| a.to_string()), - ); - - apply_optional_value( - table, - "github-releases-submodule-path", - "# Read the commit to be tagged from the submodule at this path\n", - github_releases_submodule_path - .as_ref() - .map(|a| a.to_string()), - ); - - apply_string_list( - table, - "local-artifacts-jobs", - "# Local artifacts jobs to run in CI\n", - local_artifacts_jobs.as_ref(), - ); - - apply_string_list( - table, - "global-artifacts-jobs", - "# Global artifacts jobs to run in CI\n", - global_artifacts_jobs.as_ref(), - ); - - apply_string_list( - table, - "host-jobs", - "# Host jobs to run in CI\n", - host_jobs.as_ref(), - ); - - apply_string_list( - table, - "publish-jobs", - "# Publish jobs to run in CI\n", - publish_jobs.as_ref(), - ); - - apply_string_list( - table, - "post-announce-jobs", - "# Post-announce jobs to run in CI\n", - post_announce_jobs.as_ref(), - ); - - apply_optional_value( - table, - "publish-prereleases", - "# Whether to publish prereleases to package managers\n", - *publish_prereleases, - ); - - apply_optional_value( - table, - "force-latest", - "# Always mark releases as latest, ignoring semver semantics\n", - *force_latest, - ); - - apply_optional_value( - table, - "github-attestations", - "# Whether to enable GitHub Attestations\n", - *github_attestations, - ); - - apply_string_or_list( - table, - "hosting", - "# Where to host releases\n", - hosting.as_ref(), - ); - - apply_optional_value( - table, - "install-updater", - "# Whether to install an updater program\n", - *install_updater, - ); - - apply_optional_value( - table, - "always-use-latest-updater", - "# Whether to always use the latest updater instead of a specific known-good version\n", - *always_use_latest_updater, - ); - - apply_optional_value( - table, - "display", - "# Whether to display this app's installers/artifacts in release bodies\n", - *display, - ); - - apply_optional_value( - table, - "display-name", - "# Custom display name to use for this app in release bodies\n", - display_name.as_ref(), - ); - - - - */ - // Finalize the table table.decor_mut().set_prefix("\n# Config for 'dist'\n"); }