ci: publish the .deb and the .rpm to the forge

ac9f55f · Gabriel Morell · 2026-09-17 03:21

3 files +146 -5
Message
{commit_body(@commit)}

Files changed

modified .github/workflows/rust-ci.yml
+146 −5
@@ -78,11 +78,45 @@ jobs:
78 78 - name: Strip binary
79 79 run: strip target/release/ppl
80 80
81 − - name: Install cargo-deb
82 − run: cargo install cargo-deb
81 + # Built with `stable`, not this project's pinned toolchain. The
82 + # pin says what *this crate* compiles with; cargo-deb and
83 + # cargo-generate-rpm are build-time utilities that never link
84 + # into the binary, and constraining them by the project's MSRV
85 + # breaks the install every time the ecosystem moves past it —
86 + # cargo-deb 3.8.0 needs rustc 1.88 while declaring 1.85.
87 + # `--locked` keeps two runs a month apart installing the same
88 + # bytes.
89 + - name: Install the packaging tools
90 + run: |
91 + set -euo pipefail
92 + rustup toolchain install stable --profile minimal
93 + cargo +stable install cargo-deb --locked
94 + cargo +stable install cargo-generate-rpm --locked
95 +
96 + # Every CI build has to carry a distinct version, or the second
97 + # one publishes the same filename with different bytes and the
98 + # registry correctly refuses it with a 409 — and no machine that
99 + # installed the first ever sees the second.
100 + #
101 + # `+` rather than `-`: RPM forbids `-` in a version outright, and
102 + # in both ecosystems `0.1.0+dev.<stamp>` sorts *after* 0.1.0,
103 + # which is what you want from a repository you added in order to
104 + # run the latest build. A tag publishes the bare Cargo version.
105 + - name: Stamp the package version
106 + run: |
107 + set -euo pipefail
108 + base=$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2)
83 109
84 − - name: Install cargo-generate-rpm
85 − run: cargo install cargo-generate-rpm
110 + if [ "${GITHUB_REF_TYPE:-}" = "tag" ]; then
111 + version="$base"
112 + else
113 + version="$base+dev.$(date -u +%s)"
114 + fi
115 +
116 + echo "version: $version"
117 + # Rewritten after the release build, so the binary is cached
118 + # on the unstamped manifest and only the packaging sees this.
119 + sed -i "0,/^version = .*/s//version = \"$version\"/" Cargo.toml
86 120
87 121 - name: Build test Debian package
88 122 run: cargo deb --no-build --no-strip
@@ -90,20 +124,116 @@ jobs:
90 124 - name: Build RPM package
91 125 run: cargo generate-rpm
92 126
127 + # `vars.CI_FORGE_URL` is what the forge injects into a run; fall
128 + # back to what the runner already knows. Not publishing is a
129 + # legitimate outcome — github.com has no registry at these paths.
130 + - name: Resolve the forge URL
131 + env:
132 + CI_FORGE_URL: ${{ vars.CI_FORGE_URL }}
133 + run: |
134 + set -euo pipefail
135 + forge_url=${CI_FORGE_URL:-${GITHUB_SERVER_URL:-}}
136 +
137 + case "${forge_url:-}" in
138 + "")
139 + echo "No forge URL available; not publishing."
140 + echo "PUBLISH=false" >> "$GITHUB_ENV"
141 + ;;
142 + *://github.com|*://github.com/*|*://www.github.com*)
143 + echo "Running on GitHub, which has no package registry here."
144 + echo "PUBLISH=false" >> "$GITHUB_ENV"
145 + ;;
146 + *)
147 + echo "publishing to $forge_url"
148 + echo "FORGE_URL=$forge_url" >> "$GITHUB_ENV"
149 + echo "PUBLISH=true" >> "$GITHUB_ENV"
150 + ;;
151 + esac
152 +
153 + # `${{ github.token }}` is minted for this job and authorized to
154 + # publish to THIS repository's packages and nowhere else, so
155 + # there is no secret to create first.
156 + #
157 + # Pull requests don't publish: a PR build is a check, and a
158 + # registry that carries every proposed change is not a registry
159 + # anyone can install from.
160 + - name: Publish the .deb and the .rpm to the forge
161 + if: github.event_name != 'pull_request' && env.PUBLISH == 'true'
162 + env:
163 + REPOSITORY: ${{ github.repository }}
164 + TOKEN: ${{ github.token }}
165 + run: |
166 + set -euo pipefail
167 +
168 + for deb in target/debian/*.deb; do
169 + echo "publishing $deb"
170 + curl --fail-with-body -sS \
171 + -u "gitgud-actions:$TOKEN" \
172 + -H "Content-Type: application/vnd.debian.binary-package" \
173 + --data-binary "@$deb" \
174 + "$FORGE_URL/pkg/deb/$REPOSITORY/upload?dist=stable&component=main"
175 + echo
176 + done
177 +
178 + for rpm in target/generate-rpm/*.rpm; do
179 + echo "publishing $rpm"
180 + curl --fail-with-body -sS \
181 + -u "gitgud-actions:$TOKEN" \
182 + -H "Content-Type: application/x-rpm" \
183 + --data-binary "@$rpm" \
184 + "$FORGE_URL/pkg/rpm/$REPOSITORY/upload"
185 + echo
186 + done
187 +
188 + - name: How to install it
189 + if: github.event_name != 'pull_request' && env.PUBLISH == 'true'
190 + env:
191 + OWNER: ${{ github.repository_owner }}
192 + run: |
193 + cat <<EOF
194 + # Debian / Ubuntu
195 + curl -fsSL $FORGE_URL/pkg/_keys/deb/$OWNER | sudo tee /etc/apt/keyrings/$OWNER.asc >/dev/null
196 + echo "deb [signed-by=/etc/apt/keyrings/$OWNER.asc] $FORGE_URL/pkg/deb/$OWNER/_all stable main" | sudo tee /etc/apt/sources.list.d/$OWNER.list
197 + sudo apt update && sudo apt install ppl
198 +
199 + # Fedora / RHEL / openSUSE — /etc/yum.repos.d/$OWNER.repo
200 + [$OWNER]
201 + name=$OWNER
202 + baseurl=$FORGE_URL/pkg/rpm/$OWNER/_all
203 + enabled=1
204 + repo_gpgcheck=1
205 + # cargo-generate-rpm produces unsigned packages; the forge
206 + # signs the index, not what it was handed.
207 + gpgcheck=0
208 + gpgkey=$FORGE_URL/pkg/_keys/rpm/$OWNER
209 + EOF
210 +
93 211 - name: Upload test artifact
94 212 uses: actions/upload-artifact@v4
213 + # Artifact storage is a GitHub feature; this forge has none, so
214 + # these steps have nothing to upload to. They used to run and
215 + # fail with `continue-on-error`, which is a red step on every
216 + # single run — and a build that is always a bit red teaches
217 + # everyone to stop reading it. Skipping says the same thing
218 + # honestly and leaves the badge meaningful.
219 + #
220 + # The packages are published to the forge's registries instead,
221 + # which is the thing anyone actually installs from.
222 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
95 223 with:
96 224 name: ppl-test-${{ matrix.arch }}-linux
97 225 path: target/release/ppl
98 226
99 227 - name: Upload test Debian package
100 228 uses: actions/upload-artifact@v4
229 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
101 230 with:
102 231 name: ppl-test-${{ matrix.arch }}-linux.deb
103 232 path: target/debian/*.deb
104 233
105 234 - name: Upload test RPM package
106 235 uses: actions/upload-artifact@v4
236 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
107 237 with:
108 238 name: ppl-test-${{ matrix.arch }}-linux.rpm
109 239 path: target/generate-rpm/*.rpm
@@ -217,6 +347,7 @@ jobs:
217 347
218 348 - name: Upload release archive
219 349 uses: actions/upload-artifact@v4
350 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
220 351 with:
221 352 name: ${{ matrix.name }}
222 353 path: ${{ matrix.name }}
@@ -224,6 +355,7 @@ jobs:
224 355 - name: Upload Debian package
225 356 if: runner.os == 'Linux'
226 357 uses: actions/upload-artifact@v4
358 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
227 359 with:
228 360 name: ppl-${{ matrix.os }}.deb
229 361 path: target/debian/*.deb
@@ -231,6 +363,7 @@ jobs:
231 363 - name: Upload RPM package
232 364 if: runner.os == 'Linux'
233 365 uses: actions/upload-artifact@v4
366 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
234 367 with:
235 368 name: ppl-${{ matrix.os }}.rpm
236 369 path: target/generate-rpm/*.rpm
@@ -238,7 +371,14 @@ jobs:
238 371 create-release:
239 372 name: Create Release
240 373 runs-on: arc-runner-set
241 − if: startsWith(github.ref, 'refs/tags/v')
374 + # Makes a *GitHub* Release out of artifacts a GitHub runner uploaded.
375 + # Off GitHub it would download nothing and then fail, which is the
376 + # permanently-red build this workflow is trying to stop having.
377 + # Tagged builds still publish their packages — that happens in the
378 + # build job, which runs everywhere.
379 + if: >-
380 + startsWith(github.ref, 'refs/tags/v') &&
381 + startsWith(github.server_url, 'https://github.com')
242 382 needs: release
243 383 permissions:
244 384 contents: write
@@ -247,6 +387,7 @@ jobs:
247 387
248 388 - name: Download all artifacts
249 389 uses: actions/download-artifact@v4
390 + if: ${{ startsWith(github.server_url, 'https://github.com') }}
250 391 with:
251 392 path: artifacts
252 393

Parents: 0e285b5