Skip to content

Commit 14a41e8

Browse files
[Infra] Used cargo-dist to Generate Release Scripts
Dist is a tool used to automatically create release candidates for Rust projects. Added it to this project so we can make binary releases which are easy for people to install without having to install Rust and compiling themselves.
1 parent 7998b1c commit 14a41e8

File tree

4 files changed

+319
-0
lines changed

4 files changed

+319
-0
lines changed

.github/workflows/release.yml

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
2+
#
3+
# Copyright 2022-2024, axodotdev
4+
# SPDX-License-Identifier: MIT or Apache-2.0
5+
#
6+
# CI that:
7+
#
8+
# * checks for a Git Tag that looks like a release
9+
# * builds artifacts with dist (archives, installers, hashes)
10+
# * uploads those artifacts to temporary workflow zip
11+
# * on success, uploads the artifacts to a GitHub Release
12+
#
13+
# Note that the GitHub Release will be created with a generated
14+
# title/body based on your changelogs.
15+
16+
name: Release
17+
permissions:
18+
"contents": "write"
19+
20+
# This task will run whenever you push a git tag that looks like a version
21+
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
22+
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
23+
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
24+
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
25+
#
26+
# If PACKAGE_NAME is specified, then the announcement will be for that
27+
# package (erroring out if it doesn't have the given version or isn't dist-able).
28+
#
29+
# If PACKAGE_NAME isn't specified, then the announcement will be for all
30+
# (dist-able) packages in the workspace with that version (this mode is
31+
# intended for workspaces with only one dist-able package, or with all dist-able
32+
# packages versioned/released in lockstep).
33+
#
34+
# If you push multiple tags at once, separate instances of this workflow will
35+
# spin up, creating an independent announcement for each one. However, GitHub
36+
# will hard limit this to 3 tags per commit, as it will assume more tags is a
37+
# mistake.
38+
#
39+
# If there's a prerelease-style suffix to the version, then the release(s)
40+
# will be marked as a prerelease.
41+
on:
42+
pull_request:
43+
push:
44+
tags:
45+
- '**[0-9]+.[0-9]+.[0-9]+*'
46+
47+
jobs:
48+
# Run 'dist plan' (or host) to determine what tasks we need to do
49+
plan:
50+
runs-on: "ubuntu-22.04"
51+
outputs:
52+
val: ${{ steps.plan.outputs.manifest }}
53+
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
54+
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
55+
publishing: ${{ !github.event.pull_request }}
56+
env:
57+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58+
steps:
59+
- uses: actions/checkout@v4
60+
with:
61+
persist-credentials: false
62+
submodules: recursive
63+
- name: Install dist
64+
# we specify bash to get pipefail; it guards against the `curl` command
65+
# failing. otherwise `sh` won't catch that `curl` returned non-0
66+
shell: bash
67+
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.2/cargo-dist-installer.sh | sh"
68+
- name: Cache dist
69+
uses: actions/upload-artifact@v4
70+
with:
71+
name: cargo-dist-cache
72+
path: ~/.cargo/bin/dist
73+
# sure would be cool if github gave us proper conditionals...
74+
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
75+
# functionality based on whether this is a pull_request, and whether it's from a fork.
76+
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
77+
# but also really annoying to build CI around when it needs secrets to work right.)
78+
- id: plan
79+
run: |
80+
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
81+
echo "dist ran successfully"
82+
cat plan-dist-manifest.json
83+
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
84+
- name: "Upload dist-manifest.json"
85+
uses: actions/upload-artifact@v4
86+
with:
87+
name: artifacts-plan-dist-manifest
88+
path: plan-dist-manifest.json
89+
90+
# Build and packages all the platform-specific things
91+
build-local-artifacts:
92+
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
93+
# Let the initial task tell us to not run (currently very blunt)
94+
needs:
95+
- plan
96+
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
97+
strategy:
98+
fail-fast: false
99+
# Target platforms/runners are computed by dist in create-release.
100+
# Each member of the matrix has the following arguments:
101+
#
102+
# - runner: the github runner
103+
# - dist-args: cli flags to pass to dist
104+
# - install-dist: expression to run to install dist on the runner
105+
#
106+
# Typically there will be:
107+
# - 1 "global" task that builds universal installers
108+
# - N "local" tasks that build each platform's binaries and platform-specific installers
109+
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
110+
runs-on: ${{ matrix.runner }}
111+
container: ${{ matrix.container && matrix.container.image || null }}
112+
env:
113+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114+
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
115+
steps:
116+
- name: enable windows longpaths
117+
run: |
118+
git config --global core.longpaths true
119+
- uses: actions/checkout@v4
120+
with:
121+
persist-credentials: false
122+
submodules: recursive
123+
- name: Install Rust non-interactively if not already installed
124+
if: ${{ matrix.container }}
125+
run: |
126+
if ! command -v cargo > /dev/null 2>&1; then
127+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
128+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
129+
fi
130+
- name: Install dist
131+
run: ${{ matrix.install_dist.run }}
132+
# Get the dist-manifest
133+
- name: Fetch local artifacts
134+
uses: actions/download-artifact@v4
135+
with:
136+
pattern: artifacts-*
137+
path: target/distrib/
138+
merge-multiple: true
139+
- name: Install dependencies
140+
run: |
141+
${{ matrix.packages_install }}
142+
- name: Build artifacts
143+
run: |
144+
# Actually do builds and make zips and whatnot
145+
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
146+
echo "dist ran successfully"
147+
- id: cargo-dist
148+
name: Post-build
149+
# We force bash here just because github makes it really hard to get values up
150+
# to "real" actions without writing to env-vars, and writing to env-vars has
151+
# inconsistent syntax between shell and powershell.
152+
shell: bash
153+
run: |
154+
# Parse out what we just built and upload it to scratch storage
155+
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
156+
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
157+
echo "EOF" >> "$GITHUB_OUTPUT"
158+
159+
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
160+
- name: "Upload artifacts"
161+
uses: actions/upload-artifact@v4
162+
with:
163+
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
164+
path: |
165+
${{ steps.cargo-dist.outputs.paths }}
166+
${{ env.BUILD_MANIFEST_NAME }}
167+
168+
# Build and package all the platform-agnostic(ish) things
169+
build-global-artifacts:
170+
needs:
171+
- plan
172+
- build-local-artifacts
173+
runs-on: "ubuntu-22.04"
174+
env:
175+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
176+
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
177+
steps:
178+
- uses: actions/checkout@v4
179+
with:
180+
persist-credentials: false
181+
submodules: recursive
182+
- name: Install cached dist
183+
uses: actions/download-artifact@v4
184+
with:
185+
name: cargo-dist-cache
186+
path: ~/.cargo/bin/
187+
- run: chmod +x ~/.cargo/bin/dist
188+
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
189+
- name: Fetch local artifacts
190+
uses: actions/download-artifact@v4
191+
with:
192+
pattern: artifacts-*
193+
path: target/distrib/
194+
merge-multiple: true
195+
- id: cargo-dist
196+
shell: bash
197+
run: |
198+
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
199+
echo "dist ran successfully"
200+
201+
# Parse out what we just built and upload it to scratch storage
202+
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
203+
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
204+
echo "EOF" >> "$GITHUB_OUTPUT"
205+
206+
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
207+
- name: "Upload artifacts"
208+
uses: actions/upload-artifact@v4
209+
with:
210+
name: artifacts-build-global
211+
path: |
212+
${{ steps.cargo-dist.outputs.paths }}
213+
${{ env.BUILD_MANIFEST_NAME }}
214+
# Determines if we should publish/announce
215+
host:
216+
needs:
217+
- plan
218+
- build-local-artifacts
219+
- build-global-artifacts
220+
# Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine)
221+
if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
222+
env:
223+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
224+
runs-on: "ubuntu-22.04"
225+
outputs:
226+
val: ${{ steps.host.outputs.manifest }}
227+
steps:
228+
- uses: actions/checkout@v4
229+
with:
230+
persist-credentials: false
231+
submodules: recursive
232+
- name: Install cached dist
233+
uses: actions/download-artifact@v4
234+
with:
235+
name: cargo-dist-cache
236+
path: ~/.cargo/bin/
237+
- run: chmod +x ~/.cargo/bin/dist
238+
# Fetch artifacts from scratch-storage
239+
- name: Fetch artifacts
240+
uses: actions/download-artifact@v4
241+
with:
242+
pattern: artifacts-*
243+
path: target/distrib/
244+
merge-multiple: true
245+
- id: host
246+
shell: bash
247+
run: |
248+
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
249+
echo "artifacts uploaded and released successfully"
250+
cat dist-manifest.json
251+
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
252+
- name: "Upload dist-manifest.json"
253+
uses: actions/upload-artifact@v4
254+
with:
255+
# Overwrite the previous copy
256+
name: artifacts-dist-manifest
257+
path: dist-manifest.json
258+
# Create a GitHub Release while uploading all files to it
259+
- name: "Download GitHub Artifacts"
260+
uses: actions/download-artifact@v4
261+
with:
262+
pattern: artifacts-*
263+
path: artifacts
264+
merge-multiple: true
265+
- name: Cleanup
266+
run: |
267+
# Remove the granular manifests
268+
rm -f artifacts/*-dist-manifest.json
269+
- name: Create GitHub Release
270+
env:
271+
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
272+
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
273+
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
274+
RELEASE_COMMIT: "${{ github.sha }}"
275+
run: |
276+
# Write and read notes from a file to avoid quoting breaking things
277+
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
278+
279+
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
280+
281+
announce:
282+
needs:
283+
- plan
284+
- host
285+
# use "always() && ..." to allow us to wait for all publish jobs while
286+
# still allowing individual publish jobs to skip themselves (for prereleases).
287+
# "host" however must run to completion, no skipping allowed!
288+
if: ${{ always() && needs.host.result == 'success' }}
289+
runs-on: "ubuntu-22.04"
290+
env:
291+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
292+
steps:
293+
- uses: actions/checkout@v4
294+
with:
295+
persist-credentials: false
296+
submodules: recursive

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
[workspace]
22
resolver = "3"
33
members = ["fpga_arch_parser","fpga_arch_viewer"]
4+
5+
# The profile that 'dist' will build with
6+
[profile.dist]
7+
inherits = "release"
8+
lto = "thin"

dist-workspace.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[workspace]
2+
members = ["cargo:."]
3+
4+
# Config for 'dist'
5+
[dist]
6+
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
7+
cargo-dist-version = "0.30.2"
8+
# CI backends to support
9+
ci = "github"
10+
# The installers to generate for each app
11+
installers = ["shell", "powershell"]
12+
# Target platforms to build apps for (Rust target-triple syntax)
13+
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
14+
# Path that installers should place binaries in
15+
install-path = "CARGO_HOME"
16+
# Whether to install an updater program
17+
install-updater = true

fpga_arch_viewer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
name = "fpga_arch_viewer"
33
version = "0.1.0"
44
edition = "2024"
5+
repository = "https://github.com/AlexandreSinger/rust-fpga-arch-visualizer"
56

67
[dependencies]
78
fpga_arch_parser = { path = "../fpga_arch_parser" }

0 commit comments

Comments
 (0)