Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +diff --git a/artifacts.js b/artifacts.js new file mode 100644 index 000000000..2dbfc7072 --- /dev/null +++ b/artifacts.js @@ -0,0 +1,245 @@ +/* Code modified from the blender website + * https://www.blender.org/wp-content/themes/bthree/assets/js/get_os.js?x82196 + */ + +let options = { + windows64: "x86_64-pc-windows", + windows32: "i686-pc-windows", + windowsArm: "aarch64-pc-windows", + + mac64: "x86_64-apple", + mac32: "i686-apple", + macSilicon: "aarch64-apple", + + linux64: "x86_64-unknown-linux", + linux32: "i686-unknown-linux", + linuxArm: "aarch64-unknown-linux", + + // ios: "ios", + // android: "linux-android", + // freebsd: "freebsd", +}; + +function isAppleSilicon() { + try { + var glcontext = document.createElement("canvas").getContext("webgl"); + var debugrenderer = glcontext + ? glcontext.getExtension("WEBGL_debug_renderer_info") + : null; + var renderername = + (debugrenderer && + glcontext.getParameter(debugrenderer.UNMASKED_RENDERER_WEBGL)) || + ""; + if (renderername.match(/Apple M/) || renderername.match(/Apple GPU/)) { + return true; + } + + return false; + } catch (e) {} +} + +function getOS() { + var OS = options.windows64.default; + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + if (navigator.appVersion.includes("Win")) { + if ( + !userAgent.includes("Windows NT 5.0") && + !userAgent.includes("Windows NT 5.1") && + (userAgent.indexOf("Win64") > -1 || + platform == "Win64" || + userAgent.indexOf("x86_64") > -1 || + userAgent.indexOf("x86_64") > -1 || + userAgent.indexOf("amd64") > -1 || + userAgent.indexOf("AMD64") > -1 || + userAgent.indexOf("WOW64") > -1) + ) { + OS = options.windows64; + } else { + if ( + window.external && + window.external.getHostEnvironmentValue && + window.external + .getHostEnvironmentValue("os-architecture") + .includes("ARM64") + ) { + OS = options.windowsArm; + } else { + try { + var canvas = document.createElement("canvas"); + var gl = canvas.getContext("webgl"); + + var debugInfo = gl.getExtension("WEBGL_debug_renderer_info"); + var renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); + if (renderer.includes("Qualcomm")) OS = options.windowsArm; + } catch (e) {} + } + } + } + + //MacOS, MacOS X, macOS + if (navigator.appVersion.includes("Mac")) { + if ( + navigator.userAgent.includes("OS X 10.5") || + navigator.userAgent.includes("OS X 10.6") + ) { + OS = options.mac32; + } else { + OS = options.mac64; + + const isSilicon = isAppleSilicon(); + if (isSilicon) { + OS = options.macSilicon; + } + } + } + + // linux + if (platform.includes("Linux")) { + OS = options.linux64; + // FIXME: Can we find out whether linux 32-bit or ARM are used? + } + + // if ( + // userAgent.includes("iPad") || + // userAgent.includes("iPhone") || + // userAgent.includes("iPod") + // ) { + // OS = options.ios; + // } + // if (platform.toLocaleLowerCase().includes("freebsd")) { + // OS = options.freebsd; + // } + + return OS; +} + +let os = getOS(); +window.os = os; + +// Unhide and hydrate selector with events +const archSelect = document.querySelector(".arch-select"); +if (archSelect) { + archSelect.classList.remove("hidden"); + const selector = document.querySelector("#install-arch-select"); + if (selector) { + selector.addEventListener("change", onArchChange); + } +} + +// Hydrate tab buttons with events +Array.from(document.querySelectorAll(".install-tab[data-id]")).forEach((tab) => { + tab.addEventListener("click", onTabClick); +}); + +function onArchChange(evt) { + // Get target + const target = evt.currentTarget.value; + // Find corresponding installer lists + const newContentEl = document.querySelector(`.arch[data-arch=${target}]`); + const oldContentEl = document.querySelector(`.arch[data-arch]:not(.hidden)`); + // Hide old content element (if applicable) + if (oldContentEl) { + oldContentEl.classList.add("hidden"); + } + // Show new content element + newContentEl.classList.remove("hidden"); + // Show the first tab's content if nothing was selected before + if (newContentEl.querySelectorAll(".install-tab.selected").length === 0) { + const firstContentChild = newContentEl.querySelector(".install-content:first-of-type"); + const firstTabChild = newContentEl.querySelector(".install-tab:first-of-type"); + firstContentChild.classList.remove("hidden"); + if (firstTabChild) { + firstTabChild.classList.add("selected"); + } + } + // Hide "no OS detected" message + const noDetectEl = document.querySelector(".no-autodetect"); + noDetectEl.classList.add("hidden"); + // Hide Mac hint + document.querySelector(".mac-switch").classList.add("hidden"); +} + +function onTabClick(evt) { + // Get target and ID + const {triple, id} = evt.currentTarget.dataset; + if (triple) { + // Find corresponding content elements + const newContentEl = document.querySelector(`.install-content[data-id="${String(id)}"][data-triple=${triple}]`); + const oldContentEl = document.querySelector(`.install-content[data-triple=${triple}][data-id]:not(.hidden)`); + // Find old tab to unselect + const oldTabEl = document.querySelector(`.install-tab[data-triple=${triple}].selected`); + // Hide old content element + if (oldContentEl && oldTabEl) { + oldContentEl.classList.add("hidden"); + oldTabEl.classList.remove("selected"); + } + + // Unhide new content element + newContentEl.classList.remove("hidden"); + // Select new tab element + evt.currentTarget.classList.add("selected"); + } +} + +const allPlatforms = Array.from(document.querySelectorAll(`.arch[data-arch]`)); +let hit = allPlatforms.find( + (a) => { + // Show Intel Mac downloads if no M1 Mac downloads are available + if ( + a.attributes["data-arch"].value.includes(options.mac64) && + os.includes(options.macSilicon) && + !allPlatforms.find(p => p.attributes["data-arch"].value.includes(options.macSilicon))) { + // Unhide hint + document.querySelector(".mac-switch").classList.remove("hidden"); + return true; + } + return a.attributes["data-arch"].value.includes(os); + } +); + +if (hit) { + hit.classList.remove("hidden"); + const selectEl = document.querySelector("#install-arch-select"); + selectEl.value = hit.dataset.arch; + const firstContentChild = hit.querySelector(".install-content:first-of-type"); + const firstTabChild = hit.querySelector(".install-tab:first-of-type"); + firstContentChild.classList.remove("hidden"); + if (firstTabChild) { + firstTabChild.classList.add("selected"); + } +} else { + const noDetectEl = document.querySelector(".no-autodetect"); + if (noDetectEl) { + const noDetectElDetails = document.querySelector(".no-autodetect-details"); + if (noDetectElDetails) { + noDetectElDetails.innerHTML = `We detected you're on ${os} but there don't seem to be installers for that. ` + } + noDetectEl.classList.remove("hidden"); + } +} + +let copyButtons = Array.from(document.querySelectorAll("[data-copy]")); +if (copyButtons.length) { + copyButtons.forEach(function (element) { + element.addEventListener("click", () => { + navigator.clipboard.writeText(element.attributes["data-copy"].value); + }); + }); +} + +// Toggle for pre releases +const checkbox = document.getElementById("show-prereleases"); + +if (checkbox) { + checkbox.addEventListener("click", () => { + const all = document.getElementsByClassName("pre-release"); + + if (all) { + for (var item of all) { + item.classList.toggle("hidden"); + } + } + }); +} \ No newline at end of file diff --git a/artifacts.json b/artifacts.json new file mode 100644 index 000000000..bb276f490 --- /dev/null +++ b/artifacts.json @@ -0,0 +1 @@ +{"format_version":"0.4.0","tag":"v0.2.0","formatted_date":"Aug 30 2023 at 17:32 UTC","platforms_with_downloads":[{"target":["aarch64-apple-darwin"],"display_name":"macOS Apple Silicon","installers":[0,7,2,3]},{"target":["x86_64-apple-darwin"],"display_name":"macOS Intel","installers":[0,7,2,4]},{"target":["x86_64-pc-windows-msvc"],"display_name":"Windows x64","installers":[1,7,5]},{"target":["x86_64-unknown-linux-gnu"],"display_name":"Linux x64","installers":[0,7,6]}],"downloadable_files":[[0,{"name":"cargo-dist-aarch64-apple-darwin.tar.xz","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-aarch64-apple-darwin.tar.xz","view_path":null,"checksum_file":1},["macOS Apple Silicon"]],[4,{"name":"cargo-dist-x86_64-apple-darwin.tar.xz","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-apple-darwin.tar.xz","view_path":null,"checksum_file":5},["macOS Intel"]],[6,{"name":"cargo-dist-x86_64-pc-windows-msvc.zip","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-pc-windows-msvc.zip","view_path":null,"checksum_file":7},["Windows x64"]],[8,{"name":"cargo-dist-x86_64-unknown-linux-gnu.tar.xz","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-unknown-linux-gnu.tar.xz","view_path":null,"checksum_file":9},["Linux x64"]]],"release":{"artifacts":{"files":[{"name":"cargo-dist-aarch64-apple-darwin.tar.xz","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-aarch64-apple-darwin.tar.xz","view_path":null,"checksum_file":1},{"name":"cargo-dist-aarch64-apple-darwin.tar.xz.sha256","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-aarch64-apple-darwin.tar.xz.sha256","view_path":null,"checksum_file":null},{"name":"cargo-dist-installer.ps1","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-installer.ps1","view_path":"cargo-dist-installer.ps1.txt","checksum_file":null},{"name":"cargo-dist-installer.sh","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-installer.sh","view_path":"cargo-dist-installer.sh.txt","checksum_file":null},{"name":"cargo-dist-x86_64-apple-darwin.tar.xz","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-apple-darwin.tar.xz","view_path":null,"checksum_file":5},{"name":"cargo-dist-x86_64-apple-darwin.tar.xz.sha256","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-apple-darwin.tar.xz.sha256","view_path":null,"checksum_file":null},{"name":"cargo-dist-x86_64-pc-windows-msvc.zip","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-pc-windows-msvc.zip","view_path":null,"checksum_file":7},{"name":"cargo-dist-x86_64-pc-windows-msvc.zip.sha256","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-pc-windows-msvc.zip.sha256","view_path":null,"checksum_file":null},{"name":"cargo-dist-x86_64-unknown-linux-gnu.tar.xz","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-unknown-linux-gnu.tar.xz","view_path":null,"checksum_file":9},{"name":"cargo-dist-x86_64-unknown-linux-gnu.tar.xz.sha256","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-x86_64-unknown-linux-gnu.tar.xz.sha256","view_path":null,"checksum_file":null},{"name":"cargo-dist.rb","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist.rb","view_path":null,"checksum_file":null},{"name":"dist-manifest-schema.json","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/dist-manifest-schema.json","view_path":null,"checksum_file":null},{"name":"dist-manifest.json","download_url":"https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/dist-manifest.json","view_path":null,"checksum_file":null}],"installers":[{"label":"shell","description":"Install prebuilt binaries via shell script","method":{"type":"Run","file":3,"run_hint":"curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-installer.sh | sh"}},{"label":"powershell","description":"Install prebuilt binaries via powershell script","method":{"type":"Run","file":2,"run_hint":"irm https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-installer.ps1 | iex"}},{"label":"homebrew","description":"Install prebuilt binaries via Homebrew","method":{"type":"Run","file":null,"run_hint":"brew install axodotdev/homebrew-tap/cargo-dist"}},{"label":"tarball","description":"","method":{"type":"Download","file":0}},{"label":"tarball","description":"","method":{"type":"Download","file":4}},{"label":"zip","description":"","method":{"type":"Download","file":6}},{"label":"tarball","description":"","method":{"type":"Download","file":8}},{"label":"crates.io","description":"","method":{"type":"Run","file":null,"run_hint":"cargo install cargo-dist"}}],"targets":{"aarch64-apple-darwin":[0,7,2,3],"aarch64-pc-windows-msvc":[7],"aarch64-unknown-linux-gnu":[7],"aarch64-unknown-linux-musl":[7],"i686-apple-darwin":[7],"i686-pc-windows-msvc":[7],"i686-unknown-linux-gnu":[7],"i686-unknown-linux-musl":[7],"x86_64-apple-darwin":[0,7,2,4],"x86_64-pc-windows-msvc":[1,7,5],"x86_64-unknown-linux-gnu":[0,7,6],"x86_64-unknown-linux-musl":[7]}}},"os_script":"/cargo-dist/artifacts.js","has_checksum_files":true} \ No newline at end of file diff --git a/artifacts/index.html b/artifacts/index.html new file mode 100644 index 000000000..fe217a09a --- /dev/null +++ b/artifacts/index.html @@ -0,0 +1,282 @@ + + +
+
+cargo install cargo-dist
+
+
+
+
+
+brew install axodotdev/homebrew-tap/cargo-dist
+
+
+
+
++irm https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-installer.ps1 | iex+ + + + + + + + + Source + +
+curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0/cargo-dist-installer.sh | sh+ + + + + + + + + Source + +
File | +Platform | + +Checksum | + +
---|---|---|
cargo-dist-aarch64-apple-darwin.tar.xz | ++ + + macOS Apple Silicon + + + | + + + +checksum | + + +
cargo-dist-x86_64-apple-darwin.tar.xz | ++ + + macOS Intel + + + | + + + +checksum | + + +
cargo-dist-x86_64-pc-windows-msvc.zip | ++ + + Windows x64 + + + | + + + +checksum | + + +
cargo-dist-x86_64-unknown-linux-gnu.tar.xz | ++ + + Linux x64 + + + | + + + +checksum | + + +
This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +cargo-dist's primary role is to produce "Artifacts", either to use locally or to upload as part of a release announcement. Here's a listing of the supported kinds!
+executable-zips are the primary output of cargo-dist: a zip (or tarball) containing prebuilt executables/binaries for an app, along with additional static files like READMEs, LICENSES, and CHANGELOGs.
+When you tell us to build an App for a platform we will make an executable-zip for it. Global installers will fetch and unpack executable zips from wherever you uploaded them (currently Github Releases).
+You can modify what files get included with the include and auto-includes configs.
+Currently we always make .zip on windows and .tar.xz elsewhere. This will be made configurable.
+Some notes on how we build your executables:
+--workspace
to keep things consistent--profile=dist
--target
to cargo as instructed but it will probably just fail.
+
+This feature is currently disabled pending a rework, but basically we want to save your debuginfo in the form of pdbs, dSYMs, etc. This should automatically happen as a side-effect of requiring executable-zips with the appropriate build settings to generate them.
+Most other kinds of artifact are referred to as "installers", because they generally exist as ways of downloading and installing the binaries that were made for the executable-zips.
+ + +++NOTE: It will be helpful to read the section on cargo-dist Announcement Tags, because that is the interface boundary between cargo-release and cargo-dist. TL;DR: cargo-dist interprets a git tag of "v1.0.0" as "Announce/Release the whole workspace" (Unified Announcement) and "my-app-v1.0.0" or "my-app/v1.0.0" as "Announce/Release that one package" (Singular Announcement).
+
++NOTE: this guide assumes you're running cargo-release v0.22.0 or greater, as that version made several significant changes to default behaviours (for the better!).
+
cargo-dist intentionally doesn't handle these steps of cutting a release for you:
+There's a lot of different workflows for these things and we're happy to leave that to you. All cargo-dist cares about is that a tagged commit eventually ends up in your repo (and that the format of that commit reflects the versions/names in your Cargo.tomls).
+That said, you might find cargo-release useful because it can handle all of the above things for you in a single command like cargo release 1.0.0
. This section is dedicated to explaining how to use cargo-release with cargo-dist in various situations.
++NOTE: cargo-release will never do anything side-effectful unless you also pass it
+--execute
. Unless otherwise specified, we are discussing the behaviour when that flag is passed, but will be omitting it for safety/brevity.
In a simple project with one package, without any configuration set for cargo-release, the command cargo release 1.0.0
is roughly sugar for:
<does some basic checks for uncommitted files and upstream being ahead>
+<edits your Cargo.toml to have version 1.0.0>
+git commit -am "chore: Release my-app version 1.0.0"
+git tag v1.0.0
+cargo publish
+git push --atomic <remote-branch> refs/tags/v1.0.0
+
+(The git push --atomic
is basically a more robust version of git push && git push --tags
)
Hey neat that's basically everything I listed at the start of this section! And the tag format is exactly what cargo-dist expects for a simple project!! What a coincidence!!! 😸
+If you don't want some of these behaviours, you can disable them permanently with [workspace.metadata.release]
in your Cargo.toml, or disable temporarily with CLI flags. See the cargo-release reference for all the details but here's some important ones to only get a subset of the behaviours:
publish = false
in the config or pass --no-publish
push = false
in the config or pass --no-push
tag = false
in the config or pass --no-tag
See this section for specific details on using cargo-release with github pull requests (PRs).
+Note also that you can use [package.metadata.release]
to set configs on individual packages and not the whole workspace.
With a more complex project/workspace, cargo-release won't work as well out of the box with cargo-dist. To understand why, we need to understand the rules it applies consistently that can be strange if unexpected.
+When you run cargo release
it should follow the same rules cargo does for selecting the subset of the workspace to operate on. That is, if you were to run cargo test
, the packages that actually get tested are the same ones that cargo release
will attempt to release! I'll try to briefly summarize (imperfectly, workspaces can get really Complicated):
--workspace
, execution applies to all packages (good for making a non-virtual workspace behave more like a virtual one).-p
/--package
By default, cargo-release will create a separate git tag for every package it's releasing. The default format of these tags depends on the shape of your workspace:
+v{VERSION}
("v1.0.0").{PACKAGE_NAME}-v{VERSION}
("my-app-v1.0.0")As we'll see below, these combined behaviours have the following interactions with cargo-dist:
+Now let's dig into each of these situations in more detail.
+TLDR: cargo-release Just Works.
+cargo release 1.0.0
+
+As stated previously, cargo-release works great with cargo-dist if you have a simple project consisting of a single package (the kind of project cargo new my-app
or cargo init my-app
will create).
See the previous sections for what this will do and how to configure the behaviour if, e.g. you want to hold off on publishing to crates.io or pushing.
+The more general version of this situation -- where you have one root package and all the other workspace members are libraries that exist to implement it -- has two possible solutions depending on how you want to version/release the libraries:
+ +TLDR: cargo-release just needs you to specify which package to release.
+cargo release -p my-package 1.0.0
+
+If you have a virtual workspace (one where the root Cargo.toml isn't an actual package) and want everything in the workspace to be versioned/released independently, then cargo-dist will default to operating on all your packages at once, and you should do the same thing you would do if you were running cargo publish
: either use -p
to select the relevant packages or cd
into the subdir of that package before running the command.
Each tag will induce cargo-dist to produce an independent Announcement (Github Release) for that package.
+If the package is a library the Github Release won't have any builds/artifacts uploaded. See here for details.
+Note that we currently don't support finding/emitting Release Notes for Singular Releases (simply haven't had time to design and implement it yet).
+For cargo-release to work with tag prefixes that use a slash, you must configure it to use a different prefix for tags in Cargo.toml
.
For a virtual workspace, put the following in your root Cargo.toml:
+[workspace.metadata.release]
+tag-prefix = "{{crate_name}}/"
+
+Please refer to the cargo-release reference for further information on how you can configure cargo-release.
+TLDR: cargo-release just needs you to specify that versioning/tagging should be unified.
+# Add this config to your root Cargo.toml (virtual manifest)
+[workspace.metadata.release]
+shared-version = true
+tag-name = "v{{version}}"
+
+cargo release 1.0.0
+
+If you have a virtual workspace (one where the root Cargo.toml isn't an actual package) and want everything in the workspace to be versioned/released in lockstep with a single Unified Announcement (One Big Github Release), then you're going to need to configure cargo-release as above.
+After that it works perfectly, and cargo-release will even automagically handle publishing your packages to crates.io in the right sequence and waiting for the publishes to propagate before running the next one (no more "oops sorry the package you just published isn't actually propagated to the registry yet so the package that depends on it can't be published").
+(See the next section on non-virtual workspaces with unified versions for some grittier details on what's going on here.)
+TLDR: this is much the same as the virtual workspace case but you need to pass --workspace on the CLI.
+# Add this config to your root Cargo.toml (virtual manifest)
+[workspace.metadata.release]
+shared-version = true
+tag-name = "v{{version}}"
+
+cargo release 1.0.0 --workspace
+
+If you have a non-virtual workspace (one where the root Cargo.toml is a package) and want everything in the workspace to be versioned/released in lockstep with a single Unified Announcement (One Big Github Release), then it's almost the same as the virtual case (see the previous section).
+The one caveat is that cargo-dist is consistent to a fault here, and even though we've explicitly told it things should be versioned/tagged in lockstep, running it in the root of your project still only releases the root package, and that's not what you want!
+We need to tell it that we really meant it and pass --workspace
!
What's happening here is that cargo-release
is conceptually defined to run on each package individually, with just the "git push" step being unified. The tagging settings we're providing work because it's basically repeatedly going "oh hey I was already going to make that tag, no need to make it again". It doesn't have a proper notion of the entire workspace being released in perfect lockstep, so if you ask it to release only some of the packages it will happily oblige.
In the virtual workspace this Just Works because commands in the root directory are implicitly --workspace
.
TLDR: this is a more complicated mess because but you probably want to make the root package have the Singular Announcement format, and then you just need to be explicit about each package you want to release on the CLI:
+# Add this config to your root Cargo.toml (main package)
+[package.metadata.release]
+tag-name = "{{crate_name}}-v{{version}}"
+
+cargo release -p my-package 1.0.0
+
+If you have a non-virtual workspace (one where the root Cargo.toml is a package) and want everything in the workspace to be versioned/released independently, then the simplest approach is to make everything behave like it does in the Virtual Workspace With Independent Versions.
+However if you find yourself in this position it's likely that your workspace actually looks like:
+In this precise configuration you may be able to avoid configuration by adopting a hybrid "Partially Independent Versions" approach as described in the next section.
+TLDR: technically this Just Works but you need to be specific about what packages you're publishing and may have annoying issues in the future.
+cargo release -p my-package 1.0.0
+
+So if your workspace looks like this:
+Whenever you cargo release
the root package, it will get tagged without a prefix ("v1.0.0") and cargo-dist will create a Unified Announcement. Even though there are other packages in the workspace, cargo-dist will take this in stride because as far as it's concerned this looks exactly the same as a workspace with one package. Which is to say, it's no different from a simple project as far as cargo-dist is concerned.
Whenever you cargo release
a library, it will get tagged with a prefix ("my-lib-v1.0.0") and cargo-dist will create a minimal Singular Announcement. See here for details. In future versions we might change this default (or at least make it configurable).
I have some vague concerns that this will be wonky if you ever introduce a second application to the workspace, but honestly that's probably going to be true regardless of if you were using cargo-dist, so maybe it's fine? Really I just don't trust non-virtual workspaces...
+cargo-dist really isn't designed for this but technically you can use the Singular Library Trick if you want. If you want cargo-dist to properly support this, please let us know!
+cargo-release defaults to dry-run semantics, only doing side-effectful operations if you pass it --execute
. It will also do its best to detect problems early and error out if things seem wrong. This absolutely rules!
There are two things to keep in mind:
+Let's start with the dry-run differences. I don't know them all but the biggest one that I hit is that it doesn't fully emulate bumping the versions in your Cargo.tomls. Notably when it checks if publish
will work, it's building the current version of the packages. If your build is aware of its own version this can cause/miss problems (and you'll see funky stuff like "Upgrading my-app from 1.0.0 to 2.0.0" ... "Packaging my-app 1.0.0").
As for being aware of cargo-dist... I want to design some features for this, but I'm not quite sure what it should look like yet.
+I think in the short-term, the best I can offer you is "make a temporary git branch and tell cargo-release to --execute but not push/tag/publish, then ask cargo-dist what it thinks extremely manually". A rough sketch:
+# make a temp branch where we can mess stuff up
+git checkout -b tmp-release
+
+# ask cargo-release what it thinks should happen
+# (substitute the actual cargo-release command you'd use here)
+cargo release 1.0.0
+
+That should end with a line that looks like "Pushing main, v1.0.0 to origin". The first item is the branch it's pushing to, all the following items are all the tags it wants to push. Now that we know the tags, we can ask cargo-release to update the package versions and then ask cargo-dist what it thinks of those tags:
+# just bump versions
+cargo release 1.0.0 --execute --no-push --no-tag --no-publish
+
+# ask cargo-dist what should be produced for the given tag
+cargo dist plan --tag=<tag-you-want-to-check>
+
+If that runs successfully and prints out the artifacts you expect, that's pretty good sign running cargo-release For Real will work! (You can also try cargo dist build
if you're worried about the actual build failing.)
++In this section we will be using
+$BRANCH
and$VERSION
as placeholders for the branch you make your PR on and the version you want to release.
Many teams have policies that prevent pushing to main, and require you to open pull requests instead. This conflicts with the default behaviour of cargo-release, but it works fine with some extra flags to encourage it to defer the steps until later. Specifically, use the following to "partially" run cargo-release:
+cargo release --no-publish --no-tag --allow-branch=$BRANCH $VERSION
+
+The release process then has the following steps:
+cargo release ...
to update your Cargo.tomls and push your branchcargo release
on main to complete the process (publish and tag)Crucially, neither invocation of cargo release
will modify your main branch directly. Step 4 will only push a git tag for the commit that is already on main.
Here's what this looks in practice:
+# step 0: make a branch
+git checkout -b $BRANCH
+
+
+# step 1: update things like the changelog
+# < edit some files or whatever here >
+git commit -am "prep release"
+
+
+# step 2: have cargo-release handle tedious mechanical stuff
+# this will:
+# * do some safety checks like "git index is clean"
+# * update version numbers in your crates (and handle inter-dependencies)
+# * git commit -am "chore: release $NAME $VERSION" (one commit for the whole workspace)
+# * git push (remember we're on a branch)
+cargo release --no-publish --no-tag --allow-branch=$BRANCH $VERSION
+
+
+# step 3: open a PR and review/merge to main
+# NOTE: the above steps will result in two commits
+# we recommend using github's "merge and squash" feature to clean up
+# ...
+
+
+# step 4: remove the shackles from cargo release and RUN ON MAIN
+# this will:
+# * tag the commit
+# * push the tag
+# * publish all crates to crates.io (handles waiting for dep publishes to propagate)
+# * trigger cargo-dist when it sees the tag (if applicable)
+# THIS WON'T CREATE NEW COMMITS
+#
+# running "cargo dist plan" is totally optional, but this is is the best time to check
+# that your cargo-dist release CI will produce the desired result when you push the tag
+git checkout main
+git pull
+cargo dist plan
+cargo release
+
+
+ --check
-h, --help
-h, --help
++This manual can be regenerated with
+cargo dist help-markdown
Shippable packaging for Rust.
+See 'init', 'build' and 'plan' for the 3 most important subcommands.
+cargo dist [OPTIONS]
+
+cargo dist
-v, --verbose <VERBOSE>
How verbose logging should be (log level)
+[default: warn]
+[possible values: off, error, warn, info, debug, trace]
-o, --output-format <OUTPUT_FORMAT>
The format of the output
+[default: human]
+[possible values: human, json]
--no-local-paths
Strip local paths from output (e.g. in the dist manifest json)
+This is useful for generating a clean "full" manifest as follows:
+cargo dist manifest --artifacts=all --output-format=json --no-local-paths
-t, --target <TARGET>
Target triples we want to build
+If left unspecified we will use the values in workspace.metadata.dist, except for cargo dist init
which will select some "good defaults" for you.
-i, --installer <INSTALLER>
Installers we want to build
+If left unspecified we will use the values in workspace.metadata.dist. cargo dist init
will persist the values you pass to that location.
Possible values:
+-c, --ci <CI>
CI we want to support
+If left unspecified we will use the value in workspace.metadata.dist. cargo dist init
will persist the values you pass to that location.
Possible values:
+--tag <TAG>
The (git) tag to use for the Announcement that each invocation of cargo-dist is performing.
+This tag serves two purposes: defining which apps we are Announcing new Releases for (and therefore building binaries and installers for); and picking an id to use for certain URLs. For instance the git tag associated with a Github Release is part of the URL to fetch artifacts from that release, which needs to be known by some installers!
+Unified Announcement: VERSION selects all packages with the given version (v1.0.0, 0.1.0-prerelease.1, releases/1.2.3, ...)
+Singular Announcement: PACKAGE-VERSION or PACKAGE/VERSION selects only the given package (my-app-v1.0.0, my-app/1.0.0, release/my-app/v1.2.3-alpha, ...)
+If you use the singular version then we will only Announce/Release that package's apps (and return an error if that is not in fact the package's current version). This is appropriate for workspaces that have more than one app.
+If you use the unified version then we will assume you're Announcing/Releasing all packages in the workspace that have that version. This is appropriate for workspaces that only have one app, or for monorepos that version all their apps in lockstep.
+If you do not specify this tag we will attempt to infer it by trying to Announce/Release every app in the workspace, succeeding only if they all have the same version. The tag selected will be "v{VERSION}".
+In the future we may try to make this look at the current git tags or something?
+--allow-dirty
Allow generated files like CI scripts to be out of date
+Build artifacts
+cargo dist build [OPTIONS]
+
+-a, --artifacts <ARTIFACTS>
Which subset of the Artifacts to build
+Artifacts can be broken up into two major classes: "local" ones, which are made for each target system (executable-zips, symbols, msi installers...); and "global" ones, which are made once per app (curl-sh installers, npm package, metadata...).
+Having this distinction lets us run cargo-dist independently on multiple machines without collisions between the outputs.
+If let unspecified, we will pick a fuzzier "host" mode that builds "as much as possible" for the local system. This mode is appropriate for local testing/debugging/demoing. If no --target flags are passed on the CLI then "host" mode will try to intelligently guess which targets to build for, which may include building targets that aren't defined in your metadata.dist config (since that config may exclude the current machine!).
+The specifics of "host" mode are intentionally unspecified to enable us to provider better out-of-the-box UX for local usage. In CI environments you should always specify "global" or "local" to get consistent behaviour!
+[default: host]
+Possible values:
+cargo dist manifest
-h, --help
Print help (see a summary with '-h')
+This subcommand accepts all the global options
+Setup or update cargo-dist
+This will interactively guide you through the process of selecting configuration options and will also automatically run 'cargo dist generate' afterwards as necessary. It will also handle updating your project to a new version of cargo-dist if you're running one.
+cargo dist init [OPTIONS]
+
+-y, --yes
Automatically accept all recommended/default values
+This is equivalent to just mashing ENTER over and over during the interactive prompts.
+--no-generate
Don't automatically invoke 'cargo dist generate' at the end
+--with-json-config <WITH_JSON_CONFIG>
A path to a json file containing values to set in workspace.metadata.dist and package.metadata.dist, for building tools that edit these configs.
+This is the same toml => json format that cargo metadata
produces when reporting workspace.metadata.dist
. There is some additional hierarchy for specifying which values go to which packages, but this is currently intentionally undocumented to give us some flexibility to change it.
-h, --help
Print help (see a summary with '-h')
+This subcommand accepts all the global options
+Generate one or more pieces of configuration
+cargo dist generate [OPTIONS] [MODE]...
+
+[MODE]...
+Which type of configuration to generate
Possible values:
+--check
Check if the generated output differs from on-disk config without writing it
+-h, --help
Print help (see a summary with '-h')
+This subcommand accepts all the global options
+Generate the final build manifest without running any builds.
+This command is designed to match the exact behaviour of 'cargo dist build' when passed the same flags, which is nice for consistency but annoying for anyone who doesn't understand cargo-dist's design really well.
+Notably it will default to only talking about artifacts for the host system, and will produce paths to the build dir that may not exist (since the build wasn't run).
+'cargo dist plan' is an alias for this command that picks nicer defaults by forcing a couple flags to have specific values. You probably want that.
+cargo dist manifest [OPTIONS]
+
+-a, --artifacts <ARTIFACTS>
Which subset of the Artifacts to build
+Artifacts can be broken up into two major classes: "local" ones, which are made for each target system (executable-zips, symbols, msi installers...); and "global" ones, which are made once per app (curl-sh installers, npm package, metadata...).
+Having this distinction lets us run cargo-dist independently on multiple machines without collisions between the outputs.
+If let unspecified, we will pick a fuzzier "host" mode that builds "as much as possible" for the local system. This mode is appropriate for local testing/debugging/demoing. If no --target flags are passed on the CLI then "host" mode will try to intelligently guess which targets to build for, which may include building targets that aren't defined in your metadata.dist config (since that config may exclude the current machine!).
+The specifics of "host" mode are intentionally unspecified to enable us to provider better out-of-the-box UX for local usage. In CI environments you should always specify "global" or "local" to get consistent behaviour!
+[default: host]
+Possible values:
+cargo dist manifest
-h, --help
Print help (see a summary with '-h')
+This subcommand accepts all the global options
+Get a plan of what to build (and check project status)
+If you want to know what running your cargo-dist CI will produce, this is the command for you! This is the exact command that CI will run to make its build plan and generate dist-manifest.json (although it adds --output-format=json so that it's machine-readable).
+This is an alias for the lower-level 'manifest' command with the appropriate flags forced for asking for "everything"
+cargo dist manifest --artifacts=all --no-local-paths
+cargo dist plan [OPTIONS]
+
+-h, --help
Print help (see a summary with '-h')
+This subcommand accepts all the global options
+Print this message or the help of the given subcommand(s)
+cargo dist help [COMMAND]
+
+stderr:
+ +Here's the section where I use a bunch of Capitalized Words to indicate they are a Special Concept in cargo-dist as I try to explain how it works. These are the "advanced" docs of cargo-dist; see the guide for the "beginner" docs.
+An invocation of cargo-dist has 4 major inputs:
+[workspace.metadata.dist]
(and [package.metadata.dist]
)--tag=v1.0.0
)--artifacts=all
)The first two define the full "Universe" of your project -- the platforms/binaries/installers that cargo-dist wants to build. The second two tell cargo-dist what subset of the Universe to actually bother with.
+It's important to the structure of cargo-dist that every invocation is aware of the full Universe and how it's being subsetted, because for instance if you want a shell script installer that does platform detection and fetches binaries, it needs to know about all the binaries/platforms it has to select from, even if this particular run of cargo-dist won't build them all!
+First let's look at how cargo-dist computes the Universe.
+Each Cargo package in your workspace that has binary targets is considered an App by cargo-dist. cargo-dist exists to build Apps, so making sure you and it agree on is important! (We prefer "App" over "package" because we want the freedom to one day decouple the two concepts -- for now they are strictly equivalent.)
+Most invocations of cargo-dist will start by printing out a brief summary of the Apps that cargo-dist has found:
+ +The summary includes a list of every package in your workspace. If that package defines binaries, they will be listed underneath the package. If the package's listing is greyed out, that means cargo-dist has decided it's either Not An App or that it's not part of the current Announcement (see the Announcement section), along with a parenthetical reason for its rejection, such as: "no binaries", "publish = false", "dist = false", or "didn't match tag".
+In the above example the available Apps are "evil-workspace", "many-bin", and "third-bin". "many-bin" defines two binaries, while the other two Apps only define one.
+To match cargo-install's behaviour, if a package defines multiple binaries then they will be considered part of the same App and zips/installers for it will contain/install all of them. We figure if you went out of your way to have multiple binaries under one package (as opposed to separate packages for each), you did that for a reason! If you don't want that, make separate packages. There is currently no way to group multiple packages into a single App, although there probably will be one day.
+If you don't want a package-with-binaries to be considered an App that cargo-dist should care about, you can use Cargo's own builtin publish = false. You can also use dist = false
or dist = true
in cargo-dist's own config, which when defined will take priority over publish
.
Things like cdylibs are not picked up by cargo-dist, even though they're similar to binaries. If anyone has a usecase for this we're happy to consider it (although there's some messy issues around Cargo clobbering itself when you define two many things under one package).
+Ok so you've defined your App, but what should we actually build for it? Let's look at what cargo dist init --ci=github --installer=shell --installer=powershell --yes
dumps into your root Cargo.toml:
# Config for 'cargo dist'
+[workspace.metadata.dist]
+# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
+cargo-dist-version = "0.0.3"
+# CI backends to support
+ci = ["github"]
+# The installers to generate for each app
+installers = ["shell", "powershell"]
+# Target platforms to build apps for (Rust target-triple syntax)
+targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-darwin"]
+
+# The profile that 'cargo dist' will build with
+[profile.dist]
+inherits = "release"
+lto = "thin"
+
+The parts we're really interested in here are "installers", "targets", and [profile.dist]
.
First the easy part: profile.dist
is the profile cargo-dist will build everything with. We define a separate profile from release
so that it can be tuned more aggressively for builds that are longer or more resource-intensive without making it tedious to develop locally.
The other 3 fields are defining the various Artifacts that should be produced for each App in the workspace (because this is [workspace.metadata]
and not [package.metadata]
).
For each entry in targets
you will get a build of your App for that platform in the form of an executable-zip.
For each entry in installers
you get that kind of installer for your App. There are two classes of installer: "global" and "local". This will be explained further in the section on artifact modes, but the tl;dr is that "global" installers are one-per-App while "local" installers are one-per-platform-per-app, similar to a Github CI Matrix.
"shell" and "powershell" are both global installers. There aren't currently any implemented local installers, but those would be things like a windows "msi" or macos "dmg", where you ostensibly want individual installers for each architecture.
+cargo-dist's self-generated CI is triggered by pushing git tags with specific formats like "v1.0.0", "my-app-v1.0.0" or "my-app/v1.0.0". Each tag will trigger its own independent run of that CI workflow. That tag defines the subset of the workspace (what packages) we want to produce a single unified Announcement for (i.e. a single Github Release). Every invocation of cargo-dist in that CI run will be passed that git tag with the --tag
flag to ensure consensus on what to Announce (and therefore build and upload).
1 Git Tag = 1 cargo-dist Announcement = 1 Github Release
+Even when not running in CI, this concept of a coherent Announcement Tag is important enough that we will always try to guess one even if none is provided. The "build", "manifest", and "plan" commands will refuse to run if a coherent Announcement Tag can't be determined to help you catch problems before you start pushing to CI. If this happens you will get a printout telling you some options:
+ +Here we have the same workspace we saw in the "defining your apps" section, but we get a complaint from cargo dist manifest
:
++There are too many unrelated apps in your workspace to coherently Announce!
+Please either specify --tag, or give them all the same version
+
This introduces the one big rule for Announcements: all the Apps being Announced together have to agree on their Version. We need something to tie the announcement together and "3 random Apps with different Versions" has nothing to use! You should really just have 3 separate Announcements for those Apps. If you disagree, please let us know!
+The error goes on to recommend the two formats for the Announcement Tag:
+These two modes support the following workflows:
+In this case the error notes two valid Unified Announcements:
++++
--tag=v0.5.0
will Announce: evil-workspace, third-bin +--tag=v0.7.6
will Announce: many-bin
This tells us that evil-workspace and third-bin actually already agree on their Version. If we do want a Unified Announcement, we probably want to bring many-bin into agreement, or mark it as publish=false or dist=false.
+Although you could use extremely careful versioning in conjunction with Unified Announcements to release a weird subset of the packages in your workspace, you really shouldn't because the Github Releases will be incoherent (v0.1.0 has these random packages, v0.2.0 has these other random packages... huh?), and you're liable to create painful tag collisions.
+Normally cargo-dist will error out if the Announcement Tag selects no Apps, because it exists to build and distribute Apps and you just asked it to do nothing (which is probably a mistake). This would however create annoying CI errors if you just wanted to tag Individual Releases for your libraries. To make this more pleasant, cargo-dist will produce a very minimal build-less Announcement (and therefore Github Release) if you explicitly request a Singular Announcement that matches a library-only package. This feature is kind of half-baked, please let us know what you want to happen in this situation!
+Now that we have a coherent Announcement and therefore have selected what apps we want to Release, we need to select what artifacts we want to build (or get a manifest for). Enumerating the exact artifacts for each invocation of cargo-dist would be tedious and error-prone, so we provide the --artifacts=...
flag to specify the Artifact Mode, which is a certain subset of the Universe of all Artifacts:
Let's ignore "host" mode for a bit and focus on the other three. Each one of these is intended to be used for specific tasks.
+The "all" Artifact Mode is largely intended for the manifest
command, to get a listing of everything that would be produced if you were to push the given tag to CI. Here we check what v0.5.0 would produce for our favourite example workspace:
cargo dist manifest --tag=v0.5.0 --artifacts=all --no-local-paths
+
+
+If we add --output-format=json
we will get the dist-manifest.json
that CI uploads to your Github Release:
cargo dist manifest --tag=v0.5.0 --artifacts=all --no-local-paths --output-format=json
+
+This is the only way that CI uses the flag, but you could also use "all" with build
(the default cargo-dist command) if you want to get all the artifacts built at once, although you should probably filter the --target
s as discussed in the section on "local".
cargo dist manifest --artifacts=all --no-local-paths
is so useful/common that we provide an alias for it: cargo dist plan
. The above can be simplified to:
cargo dist plan --tag=v0.5.0
+
+cargo dist plan --tag=v0.5.0 -ojson
+
+The "global" Artifact Mode allows you to unambiguously create a task that will build all the Artifacts for your Apps that aren't platform-specific and therefore only need to be made once per App:
+cargo dist build --tag=v0.5.0 --artifacts=global --no-local-paths
+
+
+Here we see that it only results in the "shell" and "powershell" installers getting built. The code to generate these should be totally cross-platform, so any runner is suitable for the task. The CI creates one "global" task that uses linux because that's the fast/cheap one.
+The "local" Artifact Mode allows you to unambiguously create a task that will build all the Artifacts for your Apps that are platform-specific and therefore should have a copy made for every target platform.
+If you just use this flag bare, cargo-dist will respect the request and try to build for all platforms at once... and this will probably fail, because cross-compilation is hard. Each "local" run should generally use --target
to filter down the set of all supported targets to the ones you can confidently build on the current machine (rustc -vV
will tell you the "host" target platform if you're not sure).
In my case it's "x86_64-pc-windows-msvc", so let's try that:
+cargo dist build --tag=v0.5.0 --artifacts=local --target=x86_64-pc-windows-msvc --no-local-paths
+
+
+Note that you can pass --target
multiple times to select more than one. Note also that --target
is not allowed to select targets that aren't specified by the config your Cargo.toml. This ensures that global installers are consistently aware of all the platform-specific artifacts they can fetch. ("host" mode breaks this rule.) ((Also in theory --installer
should work the same for selecting specific installers but it's not well tested because there isn't any reason to ever use that outside of cargo dist init
.))
CI will spin up one "local" task for each of the major desktop platforms, grouping the targets that are easy to build on those platforms together. In the future we might want to spawn one task per target, or at least make that an option you can pick. That said, some Artifacts like macOS universal binaries may find it useful to have multiple targets built on the same machine!
+Host mode is the default "do something useful on my machine" mode. It's intended for testing and demoing cargo-dist on your project, and is never used in CI due to its intentionally fuzzy semantics.
+It's currently roughly equivalent to --artifacts=all --target=HOST_TARGET
, but HOST_TARGET is allowed to fall outside the set of targets defined in your Cargo.toml, because it's not terribly useful to tell someone trying out cargo-dist on ARM64 Linux that their platform isn't defined in the config.
In principle when we have better support for cross-compilation we might also try to build "nice" crosses like "intel apple => arm64 apple". Do not rely on the behaviour of this mode, always use one of the 3 other modes in your infra/scripts!
+If you do pass --target
in host mode then we won't do fuzzy target selection and will just build the targets you ask for like normal.
Ok so here's what goes through cargo-dist's brains when you run it:
+manifest
/plan
)CI will parse the resulting (--output-format=json
) manifest of each build
invocation to know what artifacts were produced and need to be uploaded to the Github Release.
CI will just invoke cargo-dist in the following sequence:
+cargo dist manifest --artifacts=all --output-format=json --no-local-paths
cargo dist build --artifacts=local --target=... --output-format=json
cargo dist build --artifacts=global --output-format=json
(All the upload-artifacts tasks are in parallel, and there are multiple "local" tasks to cover the target platforms.)
+ +cargo-dist accepts configuration from the following sources, in order of increasing preference (that is, CLI flags generally replace things specified in your Cargo.toml):
+[workspace.metadata.dist]
[package.metadata.dist]
As discussed in concepts, all of your config should be persistently stored in the first 3 locations so that every run of cargo-dist agrees on what "build everything" should look like. CLI flags should primarily be used to select subsets of that "everything" for an individual run of cargo-dist to care about.
+The builtin Cargo.toml fields define a lot of things that cargo-dist cares about. Here's the ones that matter:
+The name of your package will become the name cargo-dist uses to refer to your package. There is currently no notion of a "prettier display name" (if you have a use for that, let us know!).
+The version of your package is used pervasively, and cargo-dist will generally error out if you ask it to build "my-app-1.0.0" when the actual "my-app" package is set to version "1.1.0".
+If you set publish = false
in your Cargo.toml we will treat this as a hint that cargo-dist should ignore all the affected packages completely. You can override this with dist's own dist = true
config.
If you set publish-prereleases = true
, cargo-dist will publish prerelease versions to package managers such as Homebrew. By default, cargo-dist will only publish stable versions.
cargo-dist has an internal notion of an "artifact download URL" that is required for things like installers that detect the current platform and fetch binaries. If your CI backend is "github" then we will base the "artifact download URL" on the "repository" key. To be safe, we will only do this if your workspace agrees on this value. It's fine if only some packages bother setting "repository", as long as the ones that do use the exact same string. If they don't we will fail to compute an "artifact download URL", emit a warning, and ignore your request for installers that require it. (This might want to be a hard error in the future.)
+cargo-dist defaults to trying to include certain "important" static files in your executable-zips. A README is one of them.
+If you specify a path to a README file, cargo-dist will use that for all the packages it affects. If you don't, then cargo-dist will search for a README* file in the package's root directory and the workspace's root directory (preferring the package).
+cargo-dist defaults to trying to include certain "important" static files in your executable-zips. A LICENSE is one of them.
+If you specify a path to a license file, cargo-dist will use that for all packages it affects. Otherwise, cargo-dist will search for LICENSE* or UNLICENSE* files in the package's root directory and the workspace's root directory (preferring the package). If multiple are defined in the same directory, we will grab them all (this is necessary for the extremely common dual MIT/Apache license, which often results in two LICENSE-* files).
+Note that the Cargo license-file flag only accepts one path, so it can't handle the dual-license-file case. This cargo feature largely exists as an escape hatch for weird licenses which can't be described by the SPDX format of the "license" field.
+Cargo allows other tools to include their own project-wide settings in metadata tables. The one cargo-dist uses is [workspace.metadata.dist]
, which must appear in your root Cargo.toml (whether or not it's virtual). You can override them on a per-package basis with [package.metadata.dist]
, which accepts all the same fields (except for those which must be specified once globally, see the docs for each individual option).
++since 0.0.3
+
Example: cargo-dist-version = "0.0.3"
This can only be set globally
+This is added automatically by cargo dist init
, and is a recording of its own version for the sake of reproducibility and documentation. When you run generate the resulting CI scripts will use that version of cargo-dist to build your applications.
The syntax must be a valid Cargo-style SemVer Version (not a VersionReq!).
+If you delete the key, generate will just use the version of cargo-dist that's currently running.
+++since 0.0.3 (deprecated in 0.1.0)
+
Example: rust-toolchain-version = "1.67.1"
++Deprecation reason: rust-toolchain.toml is a more standard/universal mechanism for pinning toolchain versions for reproducibility. Teams without dedicated release engineers will likely benefit from unpinning their toolchain and letting the underlying CI vendor silently update them to "some recent stable toolchain", as they will get updates/improvements and are unlikely to have regressions.
+
This can only be set globally
+This is added automatically by cargo dist init
, recorded for the sake of reproducibility and documentation. It represents the "ideal" Rust toolchain to build your project with. This is in contrast to the builtin Cargo rust-version which is used to specify the minimum supported Rust version. When you run generate the resulting CI scripts will install that version of the Rust toolchain with rustup. There's nothing special about the chosen value, it's just a hardcoded "recent stable version".
The syntax must be a valid rustup toolchain like "1.60.0" or "stable" (should not specify the platform, we want to install this toolchain on all platforms).
+If you delete the key, generate won't explicitly setup a toolchain, so whatever's on the machine will be used (with things like rust-toolchain.toml behaving as normal). Before being deprecated the default was to rustup update stable
, but this is no longer the case.
++since 0.0.3
+
Example: ci = ["github"]
This can only be set globally
+This is a list of CI backends you want to support, allowing subsequent runs of generate to know what CI scripts to generate. Its presence also enables certain CI-specific features. For instance if "github" is included we'll try to generate the body for a Github Release and tell installers to fetch binaries from a Github Release. Once we introduce more CI backends we'll need to more completely rationalize what that means. In all likelihood each set of CI scripts will need to explicitly select just its own CI by passing --ci=...
for every invocation.
"github" is currently the only supported CI backend.
+cargo dist init
can set this if you pass --ci=...
++since 0.3.0
+
Example: allow-dirty = ["ci"]
This is a list of generate tasks for cargo-dist to ignore when checking if generated configuration is up to date. It's useful for users who customize their own configuration beyond cargo-dist's generated defaults and want to avoid cargo-dist overwriting it.
+++since 0.0.3
+
Example: targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc"]
This is a list of target platforms you want your application(s) to be built for. In principle this can be overridden on a per-package basis but that is not well tested.
+In v0.0.5 the only properly supported choices are:
+Future versions should hopefully introduce proper support for important targets like "musl linux".
+By default all runs of cargo-dist
will be trying to handle all platforms specified here at once. If you specify --target=...
on the CLI this will focus the run to only those platforms. As discussed in concepts, this cannot be used to specify platforms that are not listed in metadata.dist
, to ensure different runs agree on the maximum set of platforms.
++since 0.0.3
+
Example: installers = ["shell", "powershell"]
This is a list of installers you want to be made for your application(s). In principle this can be overridden on a per-package basis but that is not well tested. See the full docs on installers for the full list of values.
+See "repository" for some discussion on the "Artifact Download URL".
+++since 0.2.0
+
Example: tap = "axodotdev/homebrew-formulae"
This is the name of a GitHub repository which cargo-dist should publish the Homebrew installer to. It must already exist, and the token which creates releases must have write access.
+See the installers documentation for more information on Homebrew support.
+++since 0.0.3
+
Example: include = ["my-cool-file.txt", "../other-cool-file.txt", "./some/dir/"]
This is a list of additional files or directories to copy into the root of all executable-zips that this setting affects. The paths are relative to the directory of the Cargo.toml that you placed this setting in. Globs are not supported.
+++since 0.0.3
+
Example: auto-includes = false
Allows you to specify whether cargo-dist should auto-include README, (UN)LICENSE, and CHANGELOG/RELEASES files in executable-zips. Defaults to true.
+++since 0.0.5
+
Example: windows-archive = ".tar.gz"
Allows you to specify the file format to use for executable-zips that target windows. The default is +".zip". Supported values:
+See also unix-archive below.
+++since 0.0.5
+
Example: unix-archive = ".tar.gz"
Allows you to specify the file format to use for executable-zips that target not-windows. The default is +".tar.xz". See "windows-archive" above for a complete list of supported values.
+++since 0.0.3
+
Example: dist = false
Specifies whether cargo-dist should ignore this package. It primarily exists as an alternative for publish=false
or an override for publish=false
.
++since 0.0.6
+
Example npm-scope = "@axodotdev"
Specifies that npm installers should be published under the given scope. The leading @
is mandatory. If you newly enable the npm installer in cargo dist init
's interactive UI, then it will give you an opportunity to add the scope.
If no scope is specified the package will be global.
+++since 0.1.0
+
Example: checksum = "sha512"
Specifies how to checksum executable-zips. Supported values:
+The hashes should match the result that sha256sum and sha512sum generate. The current format is just a file containing the hash of that file and nothing else.
+Future work is planned to support more robust signed checksums.
+++since 0.1.0
+
Example: precise-builds = true
This can only be set globally
+Build only the required packages, and individually.
+See "inferring precise-builds" for the default behaviour.
+By default when we need to build anything in your workspace, we try to build your entire workspace with --workspace
. This setting tells cargo-dist to instead build each app individually.
On balance, the Rust experts we've consulted with find building with --workspace to be a safer/better default, as it provides some of the benefits of a more manual workspace-hack, without the user needing to be aware that this is a thing.
+TL;DR: cargo prefers building one copy of each dependency in a build, so if two apps in your workspace depend on e.g. serde with different features, building with --workspace, will build serde once with the features unioned together. However if you build each package individually it will more precisely build two copies of serde with different feature sets.
+The downside of using --workspace is that if your workspace has lots of example/test crates, or if you release only parts of your workspace at a time, we build a lot of gunk that's not needed, and potentially bloat up your app with unnecessary features.
+If that downside is big enough for you, this setting is a good idea.
+Although cargo-dist prefers --workspace
builds (precise-builds = false
) for the reasons stated above, it will attempt to check if that's possible, and use --package
builds if necessary (precise-builds = true
).
If you explicitly set precise-builds = false
and we determine --package
builds are required, cargo-dist will produce an error. precise-builds = true
will never produce an error.
Precise-builds are considered required when you use any of features, all-features, or default-features and not all of the packages in your workspace have the same values set.
+So for instance if you have several packages in your workspace and only one sets:
+[package.metadata.dist]
+all-features = true
+
+Then we will require precise-builds, and will pass --all-features
to only the cargo build
for that package. This setting, on the other hand:
[workspace.metadata.dist]
+all-features = true
+
+Will just make us pass --all-features
to cargo build --workspace
.
++since 0.1.0
+
Example: merge-tasks = true
This can only be set globally
+Whether we should try to merge otherwise-parallelizable tasks onto the same machine, sacrificing latency and fault-isolation for more the sake of minor effeciency gains.
+For example, if you build for x64 macos and arm64 macos, by default we will generate ci which builds those independently on separate logical machines. With this enabled we will build both of those platforms together on the same machine, making it take twice as long as any other build and making it impossible for only one of them to succeed.
+The default is false
. Before 0.1.0 it was always true
and couldn't be changed, making releases annoyingly slow (and technically less fault-isolated). This config was added to allow you to restore the old behaviour, if you really want.
++since 0.1.0
+
Example: fail-fast = true
This can only be set globally
+Whether failing tasks should make us give up on all other tasks. (defaults to false)
+When building a release you might discover that an obscure platform's build is broken. When this happens you have two options: give up on the release entirely (fail-fast = true
), or keep trying to build all the other platforms anyway (fail-fast = false
).
cargo-dist was designed around the "keep trying" approach, as we create a draft Release +and upload results to it over time, undrafting the release only if all tasks succeeded. +The idea is that even if a platform fails to build, you can decide that's acceptable +and manually undraft the release with some missing platforms.
+(Note that the dist-manifest.json is produced before anything else, and so it will assume +that all tasks succeeded when listing out supported platforms/artifacts. This may make +you sad if you do this kind of undrafting and also trust the dist-manifest to be correct.)
+Prior to 0.1.0 we didn't set the correct flags in our CI scripts to do this, but now we do. +This flag was introduced to allow you to restore the old behaviour if you prefer.
+++since 0.2.0
+
Example: create-release = false
This can only be set globally
+Whether we should create the Github Release for you in your Release CI.
+If true (default), cargo-dist will create a new Github Release and generate +a title/body for it based on your changelog.
+If false, cargo-dist will assume a draft Github Release for the current git tag +already exists with the title/body you want, and just upload artifacts to it. +At the end of a successful publish it will undraft the Github Release.
+++since 0.1.0
+
Example: install-path = "~/.my-app/"
The strategy that script installers (shell, powershell) should use for selecting a path to install things at, with 3 possible syntaxes:
+CARGO_HOME
: (default) installs as if cargo install
did it (tries $CARGO_HOME/bin/
, but if $CARGO_HOME
isn't set uses $HOME/.cargo/bin/
). Note that we do not (yet) properly update some of the extra metadata files Cargo maintains, so Cargo may be confused if you ask it to manage the binary.
~/some/subdir/
: installs to the given subdir of the user's $HOME
$SOME_VAR/some/subdir
: installs to the given subdir of the dir defined by $SOME_VAR
++NOTE:
+$HOME/some/subdir
is technically valid syntax but it won't behave the way you want on Windows, because$HOME
isn't a proper environment variable. Let us handle those details for you and just use~/subdir/
.
All of these error out if none of the required env-vars are set to a non-empty value.
+We do not currently sanitize/escape the path components (it's not really a security concern when the user is about to download+run an opaque binary anyway). In the future validation/escaping of this input will become more strict. We do appear to correctly handle spaces in paths on both windows and unix (i.e. ~/My cargo-dist Documents/bin/
works), but we won't be surprised if things misbehave on Interesting Inputs.
Future Improvements:
+(Please file an issue if you have other requirements!)
+++since 0.2.0
+
Example: features = ["serde-support", "fancy-output"]
Specifies feature-flags that should be passed to a package when building it. This lets you enable features that should be on "in production" but for whatever reason shouldn't be on by default.
+For instance for packages that are a library and a CLI binary, some developers prefer to make the library the default and the CLI opt-in. In such a case you would want to add features = ["cli"]
to your [package.metadata.dist]
.
If you use this you probably want to set it on [package.metadata.dist]
and
+not [workspace.metadata.dist]
. See "inferring precise-builds" for details.
++since 0.2.0
+
Example: default-features = false
Specifies that default features for a package should be enabled when building it (when set to false, this tells us to pass --no-default-features
to Cargo).
Defaults true.
+If you use this you probably want to set it on [package.metadata.dist]
and not [workspace.metadata.dist]
. See "inferring precise-builds" for details.
++since 0.2.0
+
Example: all-features = true
Specifies that all features for a package should be enabled when building it (when set to true this tells us to pass --all-features
to Cargo).
Defaults false.
+If you use this you probably want to set it on [package.metadata.dist]
and
+not [workspace.metadata.dist]
. See "inferring precise-builds" for details.
Several metadata.dist
configs have globally available CLI equivalents. These can be used to select a subset of metadata.dist
list for that run. If you don't pass any, it will be as-if you passed all the values in metadata.dist
. You can pass these flags multiple times to provide a list. This includes:
--target
--installer
--ci
See Artifact Modes for how you might use this kind of subsetting.
+Caveat: the default "host" Artifact Mode does something fuzzier with --target
to allow you to build binaries that are usable on the current platform. Again see Artifact Modes.
Out of the box, cargo-dist's CI configuration is designed to cover the majority of usecases. We provide some tools to customize how it works for your project.
+++since 0.3.0
+
By default, cargo-dist will run the plan step on every pull request but won't perform a full release build. If these builds are turned on, the resulting pull request artifacts won't be uploaded to a release but will be available as a download from within the CI job. To enable this, select the "upload" option from the "check your release process in pull requests" question in cargo-dist-init
or set the pr-run-mode
key to "upload"
in Cargo.toml
's cargo-dist config. For example:
pr-run-mode = "upload"
+
+++since 0.3.0
+
cargo-dist's CI can be configured to call additional jobs on top of the ones it has builtin. Currently, we support adding extra jobs to the publish step; in the future, we'll allow extending all of the lifecycle steps of the CI workflow. To add one, you need to follow two steps:
+publish-jobs
array in your Cargo.toml
's cargo-dist config, prefixed with a ./
. For example, if your job name is .github/workflows/my-publish.yml
, you would write it like this:publish-jobs = ["./my-publish"]
+
+Here's an example reusable workflow written using GitHub Actions. It won't do any real publishing, just echo text to the CI output. First, create a file named .github/workflows/publish-greeter.yml
with these contents:
name: Greeter
+
+on:
+ # Defining workflow_call means that this workflow can be called from
+ # your main workflow job
+ workflow_call:
+ # cargo-dist exposes the plan from the plan step, as a JSON string,
+ # to your job if it needs it
+ inputs:
+ plan:
+ required: true
+ type: string
+
+jobs:
+ greeter:
+ runs-on: ubuntu-latest
+ # This is optional; it exposes the plan to your job as an environment variable
+ env:
+ PLAN: ${{ inputs.plan }}
+ steps:
+ - name: Step 1
+ run: |
+ echo "Hello!"
+ echo "Plan is: ${PLAN}"
+
+Then, add the following to your publish-jobs
array:
publish-jobs = ["./publish-greeter"]
+
+Running cargo-dist init
for your tool will update your GitHub Actions configuration to make use of the new reusable workflow during the publish step.
The cargo-dist Guide is the "beginner" documentation that walks you through simple usage and then introduces more complex situations as you go. More advanced documentation can be found in "concepts".
+If you have a Cargo Workspace with a single Cargo package that cargo install
works for, and just want zips containing prebuilt binaries for the major desktop platforms, that should Just Work as described in the Way-Too-Quickstart. Things get more complicated if you want to:
Gonna be blunt and say that cargo-dist is still in early days and we still need to implement a lot of stuff to better support all the things people want to do with Shippable Builds. If what you want to do doesn't seem properly supported and we don't have an issue for it, absolutely file one so we can hash it out!
+The guide will start by explaining the simple case, and then explain the more complicated cases.
+ +cargo-dist extends Cargo to support all the extra things you want to do when building the final shippable binaries for an application, so we can turn this:
+git commit -am "Chore: 0.1.0 release"
+git tag "v0.1.0"
+git push
+git push --tags
+
+into this:
+ +Locally, cargo-dist can do the following:
+Being able to build a zip on your own machine is nice and all, but in practice you probably want to have infrastructure for building and hosting the binaries for all the platforms you care about. To help streamline this, cargo-dist has builtin support for bringing up your infra (currently only Github CI and Github Releases, but we intended to support other platforms in subsequent releases)!
+Just run cargo dist init
and it will generate its own CI scripts which:
The scripts are intentionally minimal, and each machine's job roughly amounts to "install cargo-dist", "run it exactly once", "upload the artifacts it reported". It will always be easier to do builds in CI, but we want to shrink that gap as much as possible.
+We want you to be able to copy that one cargo-dist invocation CI did, run it on your machine, and get the same results without any fuss (not to bit-level precision, but to the kinds of precision normal people expect from cargo builds). No setting up docker, no weird linux-only shell scripts that assume a bunch of tools were setup in earlier CI steps.
+The main obstacle to this is "cross-compilation is hard", but other folks are making that easier every day, and eventually we'll help you with that too.
+ +