⭐
Log in to mark this repository.
ci: publish the .deb and the .rpm to the forge
ac9f55f · Gabriel Morell · 2026-09-17 03:21
✓
Rust CI and Docker Build / Create Release (push)
✓
Rust CI and Docker Build / Release (7z a, .exe, ppl-x86_64-windows.zip, windows-latest, windows-latest, 1.92) (push)
✓
Rust CI and Docker Build / Release (tar -czf, , ppl-aarch64-linux-gnu.tar.gz, ubuntu-24.04-arm, ubuntu-24.04-arm, 1.92) (push)
✓
Rust CI and Docker Build / Release (tar -czf, , ppl-aarch64-macos.tar.gz, macos-latest, macos-latest, 1.92) (push)
✓
Rust CI and Docker Build / Release (tar -czf, , ppl-x86_64-linux-gnu.tar.gz, ubuntu-latest, arc-runner-set, 1.92) (push)
✗
Rust CI and Docker Build / build (aarch64, ubuntu-24.04-arm) (push)
⏳
Rust CI and Docker Build
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