Publish to the forge's cargo registry

f1c3bee · Gabriel Morell · 2026-09-16 18:41

4 files +115 -0
Message
{commit_body(@commit)}

Files changed

added .forgejo/workflows/publish.yml
+111 −0
@@ -0,0 +1,111 @@
1 +name: publish
2 +
3 +# A tag publishes; master only checks that a publish *would* work.
4 +#
5 +# Cargo refuses to overwrite a published version, and rightly — so a workflow
6 +# that published on every push would fail on its second run and stay failed
7 +# until someone bumped the version. The dry run on master is the useful half of
8 +# that anyway: it catches a manifest that cannot be packaged at the commit that
9 +# broke it, rather than at the tag three weeks later.
10 +on:
11 + push:
12 + branches: [master]
13 + tags: ['v*']
14 +
15 +jobs:
16 + publish:
17 + runs-on: self-hosted
18 +
19 + env:
20 + # `cargo publish` needs a host toolchain and nothing else — no ESP
21 + # anything, since esp-idf-svc is target-scoped in Cargo.toml and a host
22 + # build compiles only the pure modules.
23 + #
24 + # Installed here rather than taken from a `container:` image, because the
25 + # runner executes JavaScript actions with node inside the job container
26 + # and `rust:1.90-bookworm` has no node: `actions/checkout@v4` would die
27 + # before this workflow started. (lambent-flex-c6 hits the same wall from
28 + # the other side; see its firmware.yml.)
29 + RUST_VERSION: "1.90.0"
30 + REGISTRY_NAME: neiam-portal
31 +
32 + steps:
33 + - uses: actions/checkout@v4
34 +
35 + - name: Resolve the forge URL
36 + env:
37 + CI_FORGE_URL: ${{ vars.CI_FORGE_URL }}
38 + run: |
39 + set -euo pipefail
40 + forge_url=${CI_FORGE_URL:-${GITHUB_SERVER_URL:-}}
41 +
42 + if [ -z "$forge_url" ]; then
43 + echo "No forge URL available." >&2
44 + echo "Set a CI_FORGE_URL variable on this repository or its org." >&2
45 + exit 1
46 + fi
47 +
48 + echo "FORGE_URL=$forge_url" >> "$GITHUB_ENV"
49 +
50 + - name: Cache the toolchain and the registry
51 + uses: actions/cache@v4
52 + with:
53 + path: |
54 + .home
55 + target
56 + key: rust-${{ env.RUST_VERSION }}-${{ hashFiles('Cargo.toml') }}
57 + restore-keys: rust-${{ env.RUST_VERSION }}-
58 +
59 + - name: Rust
60 + run: |
61 + set -euo pipefail
62 + export RUSTUP_HOME="$PWD/.home/.rustup" CARGO_HOME="$PWD/.home/.cargo"
63 + echo "RUSTUP_HOME=$RUSTUP_HOME" >> "$GITHUB_ENV"
64 + echo "CARGO_HOME=$CARGO_HOME" >> "$GITHUB_ENV"
65 + echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
66 +
67 + if [ ! -x "$CARGO_HOME/bin/cargo" ]; then
68 + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
69 + | sh -s -- -y --profile minimal --default-toolchain "$RUST_VERSION"
70 + fi
71 +
72 + "$CARGO_HOME/bin/cargo" --version
73 +
74 + # The registry is per repository: its index lives under this repository's
75 + # own path, and the name a consumer registers it under is the repository
76 + # name. Cargo takes an index from configuration only, never a flag — and
77 + # this goes in CARGO_HOME rather than ./.cargo/config.toml on purpose,
78 + # because `cargo publish` refuses a dirty working tree and writing into
79 + # the checkout would make it one.
80 + - name: Point cargo at the forge
81 + run: |
82 + set -euo pipefail
83 + mkdir -p "$CARGO_HOME"
84 + cat >> "$CARGO_HOME/config.toml" <<TOML
85 +
86 + [registries.$REGISTRY_NAME]
87 + index = "sparse+$FORGE_URL/pkg/cargo/${{ github.repository }}/"
88 + TOML
89 + cat "$CARGO_HOME/config.toml"
90 +
91 + # `${{ github.token }}` is minted for this job and may write this
92 + # repository's packages and nothing else, so there is no secret to create
93 + # first. Cargo reads it from CARGO_REGISTRIES_<NAME>_TOKEN, uppercased
94 + # with every non-alphanumeric run folded to `_`.
95 + - name: Publish
96 + env:
97 + CARGO_REGISTRIES_NEIAM_PORTAL_TOKEN: ${{ github.token }}
98 + run: |
99 + set -euo pipefail
100 +
101 + if [ "${GITHUB_REF_TYPE:-branch}" = "tag" ]; then
102 + cargo publish --registry "$REGISTRY_NAME"
103 + echo
104 + echo "Published. Consumers add:"
105 + echo " [registries.$REGISTRY_NAME]"
106 + echo " index = \"sparse+$FORGE_URL/pkg/cargo/${{ github.repository }}/\""
107 + else
108 + # --dry-run still packages and verify-builds; it just does not
109 + # upload. That is the whole value of running this on master.
110 + cargo publish --registry "$REGISTRY_NAME" --dry-run
111 + fi
modified .gitignore
+4 −0
@@ -3,3 +3,7 @@
3 3 /web/node_modules
4 4 /web/dist
5 5 /.embuild
6 +# CI keeps the toolchain and cargo registry here so actions/cache can see
7 +# them. Must be ignored: `cargo publish` refuses a dirty working tree, and
8 +# ignoring it also keeps it out of the packaged .crate.
9 +/.home

Parents: b85db9e