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