1use axoproject::errors::AxoprojectError;
10use backtrace::Backtrace;
11use camino::Utf8PathBuf;
12use cargo_dist_schema::{target_lexicon::Triple, ArtifactId, TripleName};
13use color_backtrace::BacktracePrinter;
14use console::style;
15use miette::{Diagnostic, SourceOffset, SourceSpan};
16use thiserror::Error;
17
18pub type DistResult<T> = std::result::Result<T, DistError>;
20
21#[derive(Debug, Error, Diagnostic)]
23pub enum DistError {
24 #[error(transparent)]
26 Io(#[from] std::io::Error),
27
28 #[error(transparent)]
30 #[diagnostic(transparent)]
31 Asset(#[from] axoasset::AxoassetError),
32
33 #[error(transparent)]
35 #[diagnostic(transparent)]
36 Cmd(#[from] axoprocess::AxoprocessError),
37
38 #[error(transparent)]
40 #[diagnostic(transparent)]
41 Gazenot(#[from] gazenot::error::GazenotError),
42
43 #[error(transparent)]
45 #[diagnostic(transparent)]
46 Project(#[from] axoproject::errors::ProjectError),
47
48 #[error(transparent)]
50 FromUtf8Error(#[from] std::string::FromUtf8Error),
51
52 #[error(transparent)]
54 Goblin(#[from] goblin::error::Error),
55
56 #[error(transparent)]
58 FromPathBufError(#[from] camino::FromPathBufError),
59
60 #[error(transparent)]
62 DialoguerError(#[from] dialoguer::Error),
63
64 #[error(transparent)]
66 #[diagnostic(transparent)]
67 AxotagError(#[from] axotag::errors::TagError),
68
69 #[error(transparent)]
71 ParseIntError(#[from] std::num::ParseIntError),
72
73 #[error(transparent)]
75 TripleError(#[from] cargo_dist_schema::target_lexicon::ParseError),
76
77 #[error("Failed to render template")]
79 #[diagnostic(
80 help("this is a bug in dist, let us know and we'll fix it: https://github.com/axodotdev/cargo-dist/issues/new")
81 )]
82 Jinja {
83 #[source_code]
85 source: String,
86 #[label]
88 span: Option<miette::SourceSpan>,
89 #[source]
91 backtrace: JinjaErrorWithBacktrace,
92 },
93
94 #[error("WiX returned an error while building {msi}")]
96 Wix {
97 msi: String,
99 #[source]
101 details: wix::Error,
102 },
103
104 #[error("Couldn't generate main.wxs for {package}'s msi installer")]
106 WixInit {
107 package: String,
109 #[source]
111 details: wix::Error,
112 },
113
114 #[error("Malformed metadata.dist in\n{manifest_path}")]
116 #[diagnostic(help("you can find a reference for the configuration schema at https://axodotdev.github.io/cargo-dist/book/reference/config.html"))]
117 CargoTomlParse {
118 manifest_path: Utf8PathBuf,
120 #[source]
122 cause: serde_json::Error,
123 },
124
125 #[error("to update your dist config you must use the version your project is configured for")]
127 #[diagnostic(help(
128 "you're running {running_version} but the project is configured for {project_version}"
129 ))]
130 NoUpdateVersion {
131 project_version: semver::Version,
133 running_version: semver::Version,
135 },
136
137 #[error("Github CI support requires your crates to agree on the URL of your repository")]
139 CantEnableGithubUrlInconsistent {
140 #[diagnostic_source]
142 inner: AxoprojectError,
143 },
144
145 #[error("Github CI support requires you to specify the URL of your repository")]
147 #[diagnostic(help(
148 r#"Set the repository = "https://github.com/..." key in these manifests: {manifest_list}"#
149 ))]
150 CantEnableGithubNoUrl {
151 manifest_list: String,
153 },
154
155 #[error("GitHub CI support requires a GitHub repository")]
157 CantEnableGithubUrlNotGithub {
158 #[diagnostic_source]
160 inner: AxoprojectError,
161 },
162
163 #[error("The npm-scope field must be an all-lowercase value; the supplied value was {scope}")]
165 ScopeMustBeLowercase {
166 scope: String,
168 },
169
170 #[error(r#"install-path = "{path}" has an unknown format (it can either be "CARGO_HOME", "~/subdir/", or "$ENV_VAR/subdir/")"#)]
175 InstallPathInvalid {
176 path: String,
178 },
179
180 #[error(r#"install-path = "{path}" is missing a subdirectory (add a trailing slash if you want no subdirectory)"#)]
185 InstallPathEnvSlash {
186 path: String,
188 },
189
190 #[error(r#"install-path = "{path}" is missing a subdirectory (installing directly to home isn't allowed)"#)]
195 InstallPathHomeSubdir {
196 path: String,
198 },
199
200 #[error("precise-builds = false was set, but some packages have custom build features, making it impossible")]
202 #[diagnostic(help("these packages customized either features, no-default-features, or all-features:\n{packages:#?}"))]
203 PreciseImpossible {
204 packages: Vec<camino::Utf8PathBuf>,
206 },
207
208 #[error("different homebrew taps were set in your workspace, this is currently unsupported")]
210 #[diagnostic(help("these packages disagree:\n{packages:#?}"))]
211 MismatchedTaps {
212 packages: Vec<camino::Utf8PathBuf>,
214 },
215
216 #[error("different publisher settings were in your workspace, this is currently unuspported")]
218 #[diagnostic(help("these packages disagree:\n{packages:#?}"))]
219 MismatchedPublishers {
220 packages: Vec<camino::Utf8PathBuf>,
222 },
223
224 #[error("different publisher 'prereleases' settings were in your workspace, this is currently unsupported")]
226 MismatchedPrereleases,
227
228 #[error("This workspace doesn't have anything for dist to Release!")]
230 NothingToRelease {
231 #[help]
233 help: String,
234 },
235
236 #[error("There are too many unrelated apps in your workspace to coherently Announce!")]
238 TooManyUnrelatedApps {
239 #[help]
241 help: String,
242 },
243
244 #[error("{} has out of date contents and needs to be regenerated:\n{diff}", file.origin_path())]
246 #[diagnostic(help("run 'dist init' to update the file\n('allow-dirty' in Cargo.toml to ignore out of date contents)"))]
247 CheckFileMismatch {
248 file: axoasset::SourceFile,
250 diff: String,
252 },
253
254 #[error("'{generate_mode}' is marked as allow-dirty in your dist config, refusing to run")]
257 ContradictoryGenerateModes {
258 generate_mode: crate::config::GenerateMode,
260 },
261
262 #[error("{artifact_name} depends on multiple packages, which isn't yet supported")]
264 #[diagnostic(help("depends on {spec1} and {spec2}"))]
265 MultiPackage {
266 artifact_name: ArtifactId,
268 spec1: String,
270 spec2: String,
272 },
273
274 #[error("{artifact_name} has no binaries")]
276 #[diagnostic(help("This should be impossible, you did nothing wrong, please file an issue!"))]
277 NoPackage {
278 artifact_name: ArtifactId,
280 },
281
282 #[error("missing WiX GUIDs in {manifest_path}: {keys:?}")]
284 #[diagnostic(help("run 'dist init' to generate them"))]
285 MissingWixGuids {
286 manifest_path: Utf8PathBuf,
288 keys: &'static [&'static str],
290 },
291
292 #[error("{style} is not a recognized job value")]
294 #[diagnostic(help("Jobs that do not come with dist should be prefixed with ./"))]
295 UnrecognizedJobStyle {
296 style: String,
298 },
299
300 #[error("{style} is not a recognized release host")]
302 UnrecognizedHostingStyle {
303 style: String,
305 },
306
307 #[error("No GitHub hosting is defined!")]
309 #[diagnostic(help("Releases must have at least GitHub hosting for updates to be supported."))]
310 NoGitHubHosting {},
311
312 #[error("{style} is not a recognized ci provider")]
314 UnrecognizedCiStyle {
315 style: String,
317 },
318
319 #[error("{style} is not a recognized type of library")]
321 UnrecognizedLibraryStyle {
322 style: String,
324 },
325
326 #[error("unable to run linkage report for {target} on {host}")]
328 LinkageCheckInvalidOS {
329 host: String,
331 target: TripleName,
333 },
334
335 #[error("unable to run linkage report for this type of binary")]
337 LinkageCheckUnsupportedBinary,
338
339 #[error("Unable to parse environment variable as a key/value pair: {line}")]
342 #[diagnostic(help("This should be impossible, you did nothing wrong, please file an issue!"))]
343 EnvParseError {
344 line: String,
346 },
347
348 #[error("We failed to generate a source tarball for your project")]
350 #[diagnostic(help("This is probably not your fault, please file an issue!"))]
351 GitArchiveError {},
352
353 #[error("We failed to query information about the git submodule at\n{path}")]
355 #[diagnostic(help("Does a submodule exist at that path? Has it been fetched with `git submodule update --init`?"))]
356 GitSubmoduleCommitError {
357 path: String,
359 },
360
361 #[error("{tool}, required to run this task, is missing")]
363 #[diagnostic(help("Ensure {tool} is installed"))]
364 ToolMissing {
365 tool: String,
367 },
368
369 #[error("The following tools are required to run this task, but are missing:\n- {}", tools.join("\n- "))]
371 #[diagnostic(help("Please install the tools mentioned above and try again."))]
372 EnvToolsMissing {
373 tools: Vec<String>,
375 },
376
377 #[error(
379 "A build was requested for {target}, but the standalone updater isn't available for it."
380 )]
381 #[diagnostic(help("At the moment, we can only provide updater binaries for the core set of most common target triples. Please set `install-updater = false` in your config."))]
382 NoAxoupdaterForTarget {
383 target: String,
385 },
386
387 #[error("Failed to check the latest release of axoupdater")]
389 #[diagnostic(help(
390 "Is your internet connection working? If not, this may be a bug; please file an issue!"
391 ))]
392 AxoupdaterReleaseCheckFailed {},
393
394 #[error("Failed to determine compression format")]
396 #[diagnostic(help("File extension of unrecognized file was {extension}"))]
397 UnrecognizedCompression {
398 extension: String,
400 },
401
402 #[error("failed to find bin {bin_name} for {pkg_name}")]
404 #[diagnostic(help("did the above build fail?"))]
405 MissingBinaries {
406 pkg_name: String,
408 bin_name: String,
410 },
411
412 #[error("`dist selfupdate` failed; the new version isn't in the place we expected")]
414 #[diagnostic(help("This is probably not your fault, please file an issue!"))]
415 UpdateFailed {},
416
417 #[error("`dist selfupdate` needs to be run in a project")]
419 #[diagnostic(help("If you just want to update dist and not your project, pass --skip-init"))]
420 UpdateNotInWorkspace {
421 #[diagnostic_source]
423 cause: axoproject::errors::ProjectError,
424 },
425
426 #[error("Incompatible install paths configured in Cargo.toml")]
428 #[diagnostic(help("The CargoHome `install-path` configuration can't be combined with other install path strategies."))]
429 IncompatibleInstallPathConfiguration,
430
431 #[error("You specified --artifacts, disabling host mode, but specified no targets to build!")]
433 #[diagnostic(help("try adding --target={host_target}"))]
434 CliMissingTargets {
435 host_target: TripleName,
437 },
438
439 #[error("please run 'dist init' before running any other commands!")]
441 NeedsInit,
442
443 #[error("You're running dist {running_version}, but 'cargo-dist-version = {config_version}' is set in your Cargo.toml")]
445 #[diagnostic(help("Rerun 'dist init' to update to this version."))]
446 MismatchedDistVersion {
447 config_version: String,
449 running_version: String,
451 },
452
453 #[error("Failed to get get toolchain version from 'cargo -vV'")]
455 FailedCargoVersion,
456
457 #[error("Failed to parse github repo: {pair}")]
459 #[diagnostic(help("should be 'owner/repo' format"))]
460 GithubRepoPairParse {
461 pair: String,
463 },
464
465 #[error("One or more unrecognized permissions levels were specified: {levels:?}")]
467 #[diagnostic(help("recognized values are: admin, write, read"))]
468 GithubUnknownPermission {
469 levels: Vec<String>,
471 },
472
473 #[error("Unrecognized target: {target}")]
475 #[diagnostic(help("The full list of supported targets can be found here: https://axodotdev.github.io/cargo-dist/book/reference/config.html#targets"))]
476 UnrecognizedTarget {
477 target: TripleName,
479 },
480
481 #[error("Installers were requested, but app contains no installable binaries")]
483 #[diagnostic(help(
484 "The only installable files are libraries, but `install-libraries` isn't enabled."
485 ))]
486 EmptyInstaller {},
487
488 #[error("failed to load github-build-setup file")]
490 GithubBuildSetupNotFound {
491 #[diagnostic_source]
493 details: axoasset::AxoassetError,
494 },
495
496 #[error("github-build-setup file wasn't valid yaml")]
498 GithubBuildSetupParse {
499 #[diagnostic_source]
501 details: axoasset::AxoassetError,
502 },
503
504 #[error("github-build-setup file at {file_path} was invalid: {message}")]
506 #[diagnostic(help(
507 "For more details about writing build steps see: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid"
508 ))]
509 GithubBuildSetupNotValid {
510 file_path: Utf8PathBuf,
512 message: String,
514 },
515
516 #[error("The metadata.dist entry in this Cargo.toml isn't being used:\n{manifest_path}")]
518 #[diagnostic(help(
519 "You probably want to move them to the [dist] section in\n{dist_manifest_path}"
520 ))]
521 UnusedMetadata {
522 manifest_path: Utf8PathBuf,
524 dist_manifest_path: Utf8PathBuf,
526 },
527
528 #[error("Your build-command's arguments need to be split up\n{manifest}\ncommand was: [\"{command}\"]")]
530 #[diagnostic(help("the command should be split [\"like\", \"--this\", \"--array=here\"]"))]
531 SusBuildCommand {
532 manifest: Utf8PathBuf,
534 command: String,
536 },
537
538 #[error("package.json was missing a \"dist\" script\n{manifest}")]
540 #[diagnostic(help(
541 "https://axodotdev.github.io/cargo-dist/book/quickstart/javascript.html#adding-a-dist-script"
542 ))]
543 NoDistScript {
544 manifest: Utf8PathBuf,
546 },
547
548 #[error("Cross-compilation from {host} to {target} is not supported")]
550 #[diagnostic(help("{details}"))]
551 UnsupportedCrossCompile {
552 host: Triple,
554 target: Triple,
556 details: String,
558 },
559
560 #[error(
562 "Cross-compilation builds from {host} to {target} cannot be used with cargo-auditable"
563 )]
564 #[diagnostic(help("set cargo-auditable to false or don't do cross-compilation"))]
565 CannotDoCargoAuditableAndCrossCompile {
566 host: Triple,
568 target: Triple,
570 },
571
572 #[error("You're building a generic package but have a Cargo-only option enabled")]
574 #[diagnostic(help("Please disable the following from your configuration: {}", options.join(", ")))]
575 CargoOnlyBuildOptions {
576 options: Vec<String>,
578 },
579
580 #[error("dist package was missing a build-command\n{manifest}")]
582 #[diagnostic(help(
583 "https://axodotdev.github.io/cargo-dist/book/quickstart/everyone-else.html#setup"
584 ))]
585 NoBuildCommand {
586 manifest: Utf8PathBuf,
588 },
589
590 #[error(
592 "cargo package was overridden with a build-command, which isn't supported yet\n{manifest}"
593 )]
594 UnexpectedBuildCommand {
595 manifest: Utf8PathBuf,
597 },
598
599 #[error("We failed to decode the certificate stored in the CODESIGN_CERTIFICATE environment variable.")]
601 #[diagnostic(help("Is the value of this environment variable valid base64?"))]
602 CertificateDecodeError {},
603
604 #[error("A Mac .pkg installer was requested, but the config is missing")]
606 #[diagnostic(help("Please ensure a dist.mac-pkg-config section is present in your config. For more details see: https://example.com"))]
607 MacPkgConfigMissing {},
608
609 #[error("No bundle identifier was specified")]
611 #[diagnostic(help("Please either enter a bundle identifier, or disable the Mac .pkg"))]
612 MacPkgBundleIdentifierMissing {},
613
614 #[error("Your project ({package_name}) uses axoupdater as a library, but the version specified ({your_version}) is older than the minimum supported version ({minimum}). (The dependency comes via {source_name} in the dependency tree.)")]
616 #[diagnostic(help(
617 "https://axodotdev.github.io/cargo-dist/book/installers/updater.html#minimum-supported-version-checking"
618 ))]
619 AxoupdaterTooOld {
620 package_name: String,
622 source_name: String,
624 minimum: semver::Version,
626 your_version: semver::Version,
628 },
629}
630
631impl From<minijinja::Error> for DistError {
632 fn from(details: minijinja::Error) -> Self {
633 let source: String = details.template_source().unwrap_or_default().to_owned();
634 let span = details.range().map(|r| r.into()).or_else(|| {
635 details.line().map(|line| {
636 let start = SourceOffset::from_location(&source, line, 0);
638 let end = SourceOffset::from_location(&source, line + 1, 0);
639 let len = (end.offset() - start.offset()).wrapping_sub(1);
640 SourceSpan::from((start, len))
641 })
642 });
643
644 DistError::Jinja {
645 source,
646 span,
647 backtrace: JinjaErrorWithBacktrace::new(details),
648 }
649 }
650}
651#[derive(Debug)]
654pub struct JinjaErrorWithBacktrace {
655 pub error: minijinja::Error,
657 backtrace: Backtrace,
659}
660
661impl JinjaErrorWithBacktrace {
662 fn new(error: minijinja::Error) -> Self {
663 Self {
664 error,
665 backtrace: Backtrace::new_unresolved(),
666 }
667 }
668}
669
670impl std::error::Error for JinjaErrorWithBacktrace {}
671
672impl std::fmt::Display for JinjaErrorWithBacktrace {
673 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674 let mut bt = self.backtrace.clone();
675 bt.resolve();
676 let backtrace = BacktracePrinter::new()
677 .add_frame_filter(Box::new(|frames| {
678 if let Some(real_main_idx) = frames.iter().position(|f| {
680 f.name
681 .as_ref()
682 .map(|n| n.contains("real_main"))
683 .unwrap_or(false)
684 }) {
685 frames.splice(real_main_idx + 1.., vec![]);
686 }
687 }))
688 .format_trace_to_string(&bt)
689 .unwrap();
690 write!(
691 f,
692 "Backtrace:\n{}\n{}",
693 style(&backtrace).dim(),
694 style(&self.error).red()
695 )
696 }
697}