Some checks failed
Weekly OSV dependency audit / dependency-audit (push) Failing after 5s
473 lines
22 KiB
Markdown
473 lines
22 KiB
Markdown
# Portable Gitea Release Builds from a Linux ARM64 Runner
|
|
|
|
This document is a blueprint for producing native Rust application releases for
|
|
Linux ARM64, Linux x64, macOS ARM64, macOS x64, and Windows x64 from a Gitea
|
|
Actions runner whose host is Ubuntu ARM64. It records the design and the failure
|
|
modes proven while preparing the RuDS 0.9.0 release.
|
|
|
|
The important distinction is that the release tooling is portable Rust, not
|
|
that every application dependency is pure Rust. GTK and WebKitGTK are normal
|
|
Linux system libraries and should be linked from the runner's Ubuntu packages.
|
|
Apple bundles need portable construction because no macOS runner is available.
|
|
|
|
The implementation in this repository is split between:
|
|
|
|
- `.gitea/workflows/release.yml`: orchestration and target-specific build setup.
|
|
- `.gitea/scripts/install-linux-build-dependencies.sh`: native ARM64 packages
|
|
and the extracted x64 Ubuntu sysroot.
|
|
- `.gitea/scripts/x86_64-linux-gnu-gcc-pic` and
|
|
`.gitea/scripts/x86_64-linux-gnu-g++-pic`: cross-compiler wrappers.
|
|
- `crates/bds-release`: release notes, Gitea API access, dependency downloads,
|
|
archive creation, Apple bundle construction, signing, and upload.
|
|
|
|
## Resulting artifact matrix
|
|
|
|
Build CLI and desktop artifacts separately so users do not have to download a
|
|
GUI application to obtain command-line tools. RuDS creates two assets per target:
|
|
|
|
| Target | CLI | Desktop |
|
|
| --- | --- | --- |
|
|
| macOS ARM64 | `.tar.gz` | `.dmg` containing an `.app` |
|
|
| macOS x64 | `.tar.gz` | `.dmg` containing an `.app` |
|
|
| Linux ARM64 | `.tar.gz` | `.tar.gz` |
|
|
| Linux x64 | `.tar.gz` | `.tar.gz` |
|
|
| Windows x64 | `.zip` | portable `.zip` |
|
|
|
|
Compile all final programs for a target in one Cargo invocation. In this
|
|
repository those are `bds-ui`, `bds-cli`, and `bds-mcp`. This keeps their feature
|
|
selection and shared runtime libraries consistent.
|
|
|
|
## Pipeline shape
|
|
|
|
Use a tag-triggered, draft-first pipeline:
|
|
|
|
1. Check out full history, test the release helper, generate notes, and create
|
|
or update a draft Gitea release.
|
|
2. Build and upload every platform into that release ID.
|
|
3. Publish only after every build job succeeds.
|
|
4. Query Gitea afterward and verify the public release, target commit, and asset
|
|
manifest.
|
|
|
|
A minimal dependency graph is:
|
|
|
|
```text
|
|
prepare draft
|
|
|
|
|
+-- Linux x64
|
|
|
|
|
+-- Linux ARM64
|
|
+-- macOS ARM64 + x64
|
|
+-- Windows x64
|
|
|
|
|
+-- publish
|
|
```
|
|
|
|
RuDS deliberately runs the most failure-prone cross-link first. On a single
|
|
runner all jobs serialize anyway, and failing early avoids spending hours on
|
|
other targets first. With several independent runners, make all platform jobs
|
|
depend only on preparation and let them run in parallel.
|
|
|
|
The draft is the transaction boundary. Never make a release public before all
|
|
assets exist. The final publisher should ideally fetch the draft and compare
|
|
the exact expected filenames and non-zero sizes before setting `draft: false`.
|
|
RuDS currently relies on job dependencies plus strict per-package input checks;
|
|
adding an explicit final manifest check would make that boundary stronger.
|
|
|
|
## Gitea runner and permissions
|
|
|
|
Register a label that describes the real machine, for example
|
|
`linux-arm64:host`, and use `runs-on: linux-arm64`. Do not treat a name such as
|
|
`ubuntu-latest` as an implicit x64 machine. Gitea runner labels have the form
|
|
`label[:schema[:args]]`.
|
|
|
|
The runner needs:
|
|
|
|
- Enough disk for Cargo registries, several target trees, cross SDKs, sysroots,
|
|
and container layers.
|
|
- Outbound HTTPS access to Rust distribution servers, crates.io, Gitea, NuGet,
|
|
GitHub release assets, and the configured container registry.
|
|
- Passwordless access to install Ubuntu build packages, or a prebuilt runner
|
|
image containing them.
|
|
- Container support for the macOS and Windows build images.
|
|
- A URL by which build containers can reach Gitea. `localhost` inside a
|
|
container is the container itself, not the runner host.
|
|
|
|
Container images must publish an ARM64 image even when the target they build is
|
|
x64. Pin images by digest, not a floating tag. Also pin the Rust toolchain and
|
|
always build with `--locked`.
|
|
|
|
Grant the workflow only the permissions it needs: repository contents read,
|
|
issues read if release notes inspect issues, and releases write. Gitea clamps
|
|
workflow permissions to repository or owner limits. If the built-in
|
|
`secrets.GITEA_TOKEN` cannot write releases on the deployed Gitea version, use a
|
|
repository secret containing a narrowly scoped token.
|
|
|
|
Gitea Actions resembles GitHub Actions but is not identical. Validate contexts,
|
|
permissions, output files, and action compatibility against the deployed Gitea
|
|
and `act_runner` versions. Non-fully-qualified actions may be downloaded from
|
|
GitHub depending on `DEFAULT_ACTIONS_URL`; mirror or fully qualify them on
|
|
restricted installations.
|
|
|
|
Pass the draft release ID through a job output. Gitea supports the familiar
|
|
`key=value` step-output file convention; this repository writes to the
|
|
`GITHUB_OUTPUT` compatibility variable and maps that step output to a job
|
|
output. Do not scrape an ID from human-readable command output.
|
|
|
|
Specialized build containers may not run JavaScript-based checkout actions
|
|
reliably. RuDS performs an authenticated, depth-one `git fetch` of the exact tag
|
|
inside those containers. Fetch the tag ref explicitly and check out
|
|
`FETCH_HEAD`; do not build a mutable branch tip.
|
|
|
|
## Linux desktop builds
|
|
|
|
There is no architectural reason to exclude the GTK/WebKit desktop application
|
|
from Linux artifacts. The native ARM64 build is ordinary compilation on the
|
|
ARM64 Ubuntu runner. Install:
|
|
|
|
```text
|
|
build-essential
|
|
cmake
|
|
libgtk-3-dev
|
|
libwebkit2gtk-4.1-dev
|
|
libxdo-dev
|
|
pkg-config
|
|
```
|
|
|
|
The resulting application dynamically links the distro GTK/WebKit stack. Do
|
|
not put the entire GTK/WebKit dependency closure into the application archive.
|
|
Bundle application-owned shared libraries, dynamically linked Rust standard
|
|
libraries, and explicitly redistributable runtimes; document the required
|
|
Linux runtime packages for users.
|
|
|
|
Build on the oldest distro baseline that the release promises to support.
|
|
Using a rolling `ubuntu-latest` silently raises the minimum glibc and WebKitGTK
|
|
versions when the runner image changes. A fixed Ubuntu release or container is
|
|
more reproducible.
|
|
|
|
Add an `$ORIGIN` runtime search path so executables find shared libraries placed
|
|
beside them:
|
|
|
|
```text
|
|
-C link-arg=-Wl,-rpath,$ORIGIN
|
|
```
|
|
|
|
### Cross-linking Linux x64 on ARM64
|
|
|
|
Rust cross-compilation is the easy part. Native dependencies selected through
|
|
`pkg-config`, C/C++ build scripts, the linker, and the x64 libc are the parts
|
|
that require deliberate configuration.
|
|
|
|
On an Ubuntu ARM64 host:
|
|
|
|
1. Restrict the existing Ubuntu deb822 sources to `arm64`.
|
|
2. Add `amd64` as a foreign architecture and add matching amd64 archive and
|
|
security sources for the same Ubuntu codename.
|
|
3. Install native host tools such as `gcc-x86-64-linux-gnu`,
|
|
`g++-x86-64-linux-gnu`, `cmake`, and `pkg-config`.
|
|
4. Download the GTK, WebKitGTK, and xdo amd64 development packages and their
|
|
dependencies with APT's `--download-only` mode.
|
|
5. Extract the `.deb` files with `dpkg-deb --extract` into an isolated sysroot,
|
|
such as `/opt/ruds-x64-sysroot`.
|
|
6. Point Cargo, the GNU cross-linker, and `pkg-config` exclusively at that
|
|
sysroot.
|
|
|
|
Do not normally install the foreign development packages on the ARM64 host.
|
|
Some packages contain target-architecture helper executables or maintainer
|
|
scripts; package installation can attempt to execute x64 programs on ARM64.
|
|
Downloading and extracting produces the headers, libraries, and metadata needed
|
|
for linking without running target code.
|
|
|
|
The essential environment is:
|
|
|
|
```text
|
|
AR_x86_64_unknown_linux_gnu=x86_64-linux-gnu-ar
|
|
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc
|
|
PKG_CONFIG_ALLOW_CROSS=1
|
|
PKG_CONFIG_SYSROOT_DIR=/opt/ruds-x64-sysroot
|
|
PKG_CONFIG_LIBDIR=/opt/ruds-x64-sysroot/usr/lib/x86_64-linux-gnu/pkgconfig:/opt/ruds-x64-sysroot/usr/lib/pkgconfig:/opt/ruds-x64-sysroot/usr/share/pkgconfig
|
|
```
|
|
|
|
Pass `--sysroot` to the linker, add the target library directories with Cargo
|
|
`-L native=...`, and retain the `$ORIGIN` rpath. Do not leave host pkg-config
|
|
paths in the search list: that can produce an ARM64/x64 mixture which may fail
|
|
late or, worse, appear to link successfully.
|
|
|
|
Ubuntu libc development linker scripts may refer to absolute legacy paths such
|
|
as `/lib/x86_64-linux-gnu`. Those paths are interpreted inside the sysroot but
|
|
may not exist in an extracted modern Ubuntu package layout. Materialize the
|
|
required libc, libm, libmvec, and loader paths within the sysroot. Do not create
|
|
host-level x64 symlinks.
|
|
|
|
Architecture-independent `.pc` files may already be installed on the host, so
|
|
APT may not download them again for amd64. They contain metadata rather than
|
|
target executables and can be copied into the sysroot; `PKG_CONFIG_SYSROOT_DIR`
|
|
then redirects their paths.
|
|
|
|
Some native Rust dependencies build static objects that are later linked into
|
|
application shared libraries. They need position-independent code. Compiler
|
|
wrappers should append `-fPIC` as the final flag so it overrides earlier flags.
|
|
Target-specific Cargo environment names can contain hyphens and therefore are
|
|
not valid shell assignment identifiers. Set them through `env`, for example:
|
|
|
|
```sh
|
|
env \
|
|
"CC_x86_64-unknown-linux-gnu=$PWD/.gitea/scripts/x86_64-linux-gnu-gcc-pic" \
|
|
"CXX_x86_64-unknown-linux-gnu=$PWD/.gitea/scripts/x86_64-linux-gnu-g++-pic" \
|
|
cargo build --release --locked --target x86_64-unknown-linux-gnu
|
|
```
|
|
|
|
Audit every `-sys` crate for pkg-config overrides. For RuDS,
|
|
`ZSTD_SYS_USE_PKG_CONFIG` must be unset in the x64 build so zstd does not select
|
|
an incompatible host library.
|
|
|
|
## macOS without a macOS runner
|
|
|
|
Use `cargo-zigbuild` in a digest-pinned ARM64-compatible container to compile
|
|
the `aarch64-apple-darwin` and `x86_64-apple-darwin` targets. The container must
|
|
contain a legally usable Apple SDK. Review the SDK and redistribution licenses;
|
|
portable tooling does not remove those obligations.
|
|
|
|
RuDS supplies these linker requirements:
|
|
|
|
- Both `@executable_path` and `@executable_path/../Resources` rpaths. The first
|
|
serves the flat CLI archive and the second serves the `.app` layout.
|
|
- Header padding for later Mach-O load-command rewriting.
|
|
- The chosen minimum macOS deployment version.
|
|
- `-Wl,-undefined,dynamic_lookup` where Apple symbols are intentionally resolved
|
|
at runtime.
|
|
|
|
Zig's Darwin linker does not implement every `ld64` option. In particular,
|
|
symbol-specific `-U` handling was not usable here. Broad
|
|
`-undefined,dynamic_lookup` works when late-bound framework symbols are truly
|
|
required, but it can also mask accidental unresolved symbols; use it only with
|
|
dependency inspection and runtime testing.
|
|
|
|
The Zig Apple link path expected a compiler runtime archive alias. A valid empty
|
|
`libclang_rt.osx.a` archive (`!<arch>\n`) in an explicit native library directory
|
|
satisfied that lookup.
|
|
|
|
### Intel macOS and version-sensitive native runtimes
|
|
|
|
Do not assume that the latest machine-learning runtime publishes every Apple
|
|
architecture. The ONNX Runtime release used by newer Rust crates no longer had
|
|
an Intel macOS binary. RuDS keeps a compatible dependency line:
|
|
|
|
```text
|
|
fastembed 5.12.0
|
|
ort/ort-sys 2.0.0-rc.11
|
|
ONNX Runtime 1.23.2 for macOS x64
|
|
```
|
|
|
|
The Rust release helper downloads Microsoft's x64 archive, verifies its pinned
|
|
SHA-256 digest, extracts the versioned dylib and license, and creates an
|
|
unversioned linker copy. With `ort-sys` rc.11, the working build variables are:
|
|
|
|
```text
|
|
ORT_LIB_LOCATION=<directory-containing-libonnxruntime.dylib>
|
|
ORT_PREFER_DYNAMIC_LINK=1
|
|
```
|
|
|
|
`ORT_LIB_PATH`, documented or accepted by other versions, is ignored by this
|
|
specific prerelease. When a native dependency behaves unexpectedly, read the
|
|
build script for the exact locked crate version rather than documentation for a
|
|
newer release.
|
|
|
|
### Pure-Rust Apple packaging
|
|
|
|
Build the application bundle and DMG with Rust crates rather than `hdiutil`,
|
|
`codesign`, shell archive tricks, or a simulated macOS environment. RuDS uses
|
|
Rust libraries to:
|
|
|
|
1. Create `Contents/MacOS`, `Contents/Resources`, `Info.plist`, the icon, bundle
|
|
metadata, and URL handlers.
|
|
2. Put executables in `Contents/MacOS` and dylibs/licenses in Resources.
|
|
3. Parse 64-bit little-endian Mach-O load commands and rewrite application
|
|
dylib IDs and references to `@rpath/<basename>`.
|
|
4. Sign after all binary mutations.
|
|
5. Construct and sign the DMG.
|
|
|
|
Mach-O load commands have fixed allocated space. Reserve header padding at link
|
|
time or a longer rewritten install name will not fit. Rewrite every applicable
|
|
load command, including weak, re-exported, lazy, and upward dylib loads, and
|
|
never attempt to parse licenses or other resource files as Mach-O binaries.
|
|
|
|
The current output is ad-hoc signed. Developer ID signing and Apple notarization
|
|
need Apple-issued credentials and a supported submission path; a Linux-only
|
|
pipeline cannot claim notarized output merely because it can construct and
|
|
ad-hoc-sign a valid bundle.
|
|
|
|
## Windows x64 from Linux ARM64
|
|
|
|
Use `cargo-xwin` in a digest-pinned ARM64-compatible container. It provides the
|
|
Windows MSVC SDK/import-library environment without requiring a Windows host.
|
|
Install the Rust `x86_64-pc-windows-msvc` target and NASM when native assembly
|
|
dependencies require it, then populate the xwin cache before looking up SDK
|
|
libraries.
|
|
|
|
RuDS's DirectML-enabled build needs:
|
|
|
|
- Microsoft DirectML 1.15.4 downloaded from NuGet and verified by SHA-256.
|
|
- `DirectML.lib` for linking, plus `DirectML.dll` and its license in the final
|
|
package.
|
|
- A correctly cased `PathCch.lib` alias copied from the xwin SDK, because some
|
|
build tooling expects that spelling.
|
|
- The target release directory on Cargo's native library search path.
|
|
|
|
Do not force `target-feature=+crt-static` merely to make the archive look more
|
|
self-contained. Windows system and SDK dependencies may require the dynamic
|
|
CRT, and forcing static CRT can produce incompatible link combinations. Bundle
|
|
the redistributable application runtime DLLs that are actually required and
|
|
retain their licenses. RuDS statically links ONNX Runtime on Windows but ships
|
|
DirectML dynamically.
|
|
|
|
Portable ZIPs are the simplest cross-host Windows result. MSI, MSIX, or signed
|
|
installer production introduces additional tooling, signing certificates, and
|
|
Windows policy requirements and should be treated as separate scope.
|
|
|
|
## Packaging rules
|
|
|
|
Keep packaging in a small host-native Rust binary. `bds-release` runs on the
|
|
ARM64 builder even while it packages foreign target files. It uses Rust for
|
|
HTTP, hashing, tar/gzip, ZIP, plist, ICNS, Apple bundle/DMG construction, Mach-O
|
|
mutation, signing, and Gitea API calls.
|
|
|
|
For each target:
|
|
|
|
- Require every expected executable and application shared library before
|
|
creating an archive.
|
|
- Copy dynamic Rust standard libraries from
|
|
`rustc --target <target> --print target-libdir` when the project links Rust's
|
|
standard library dynamically.
|
|
- Include application-owned shared libraries and required redistributable
|
|
third-party runtimes with license files.
|
|
- Give archives deterministic, tag-derived names and a single top-level
|
|
directory.
|
|
- Reject unsafe tags used as filenames and refuse to overwrite existing output.
|
|
- Stream large uploads with a content length rather than loading them into RAM.
|
|
- Retry network failures, HTTP 429, and server errors with bounded backoff.
|
|
- Verify every downloaded native dependency with a pinned cryptographic digest.
|
|
|
|
Packaging must follow the target's runtime model. Linux should use the distro's
|
|
GTK/WebKit libraries; macOS `.app` bundles should carry their private dylibs in
|
|
Resources with corrected install names; Windows packages should carry approved
|
|
redistributable DLLs. “Put every shared library beside the executable” is not a
|
|
portable policy.
|
|
|
|
## Release creation and rebuilding
|
|
|
|
The preparation job uses full Git history to locate the previous tag, reads
|
|
issue numbers from commit messages, fetches closed issues through Gitea's API,
|
|
and creates categorized release notes. It writes the draft release ID as a step
|
|
output so every build uploads to the same release.
|
|
|
|
Rebuilding an existing published tag is deliberately explicit. In RuDS,
|
|
`REBUILD_PUBLISHED_RELEASE=true` changes the release back to a draft, deletes
|
|
its assets, regenerates its metadata, and uploads a consistent new set. This is
|
|
destructive and temporarily removes the public release. Prefer a new immutable
|
|
tag unless repairing an existing release is intentional. Rerunning a failed
|
|
draft also clears its partial asset set before rebuilding, which prevents stale
|
|
and new files from being mixed.
|
|
|
|
Before pushing a release tag:
|
|
|
|
1. Update the workspace version, lockfile, README, and any visible version
|
|
metadata together.
|
|
2. Run formatting, the full workspace tests, and relevant release-helper tests.
|
|
3. Build locally for the host where practical.
|
|
4. Commit and push the release commit to the default branch.
|
|
5. Create an annotated tag at that exact commit, then push the tag.
|
|
|
|
After the workflow finishes, verify through the Gitea API or `tea`:
|
|
|
|
- Every job is successful rather than merely the workflow appearing complete.
|
|
- The tag and its peeled commit match the intended default-branch commit.
|
|
- The release is public (`draft: false`) and has the expected prerelease flag.
|
|
- The release targets the exact tagged commit.
|
|
- The expected asset names are present exactly once and have non-zero sizes.
|
|
- The repository worktree is clean and local/remote refs agree.
|
|
|
|
Useful Gitea API endpoints are:
|
|
|
|
```text
|
|
GET /repos/{owner}/{repo}/actions/runs
|
|
GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs
|
|
GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs
|
|
GET /repos/{owner}/{repo}/releases/tags/{tag}
|
|
```
|
|
|
|
Job logs can be very large. Download or query them once, then filter locally for
|
|
the failing step and nearby compiler/linker diagnostics instead of repeatedly
|
|
printing the entire log.
|
|
|
|
## Common failures
|
|
|
|
| Symptom | Cause | Fix |
|
|
| --- | --- | --- |
|
|
| `Exec format error` during x64 dependency installation | A foreign package's executable or maintainer script ran on ARM64 | Download packages only and extract them into a sysroot |
|
|
| Linker reports wrong ELF architecture | Host libraries leaked through pkg-config or a build script | Restrict `PKG_CONFIG_LIBDIR`, set its sysroot, and audit `-sys` crate overrides |
|
|
| Relocation errors while creating a `.so` | A C/C++ static object was built without PIC | Append `-fPIC` in target compiler wrappers |
|
|
| libc or loader missing despite downloaded packages | Ubuntu linker scripts contain absolute legacy paths | Materialize those target paths inside the sysroot |
|
|
| Linux binary starts but cannot find project `.so` files | No relative runtime search path | Link with an `$ORIGIN` rpath and package the libraries beside the binary |
|
|
| macOS link rejects `-U` | Zig's Darwin linker differs from Apple `ld64` | Use supported options; use dynamic lookup only when justified |
|
|
| Mach-O install-name rewrite does not fit | No load-command header padding | Add header padding before linking, then mutate before signing |
|
|
| Intel macOS ORT download fails | The selected upstream release dropped x64 macOS | Pin a compatible crate/runtime pair and supply a verified dylib |
|
|
| `ort-sys` ignores a supplied runtime | Environment variable belongs to another crate version | Inspect the exact locked crate build script; rc.11 uses `ORT_LIB_LOCATION` |
|
|
| Windows library is “missing” despite SDK contents | Import-library case/name differs from build-script expectation | Add an explicit linker alias such as `PathCch.lib` |
|
|
| Windows CRT link conflicts | Static CRT was forced across incompatible dependencies | Use the target's normal dynamic CRT unless the full graph supports static CRT |
|
|
| Checkout fails only inside a build container | The image cannot execute the checkout action or cannot reach Gitea | Fetch the exact tag with authenticated Git in the container |
|
|
| Release is public but incomplete | Publication happened before asset completion | Create a draft first and publish only behind all build jobs and manifest checks |
|
|
| A release takes hours | Cold Rust/native builds on one physical ARM64 runner serialize | Add real runners, carefully cache immutable inputs, or prebake dependencies |
|
|
|
|
## Performance and reproducibility
|
|
|
|
A cold five-target build with GTK/WebKit, ONNX Runtime, and ML dependencies can
|
|
take about three hours on a single ARM64 runner. The workflow itself is simple;
|
|
compilation and native dependency setup dominate the time.
|
|
|
|
Safe ways to reduce it include:
|
|
|
|
- Add separate runners with the same label, or platform-specific labels, so
|
|
independent target jobs truly run concurrently.
|
|
- Prebuild a versioned runner/container image containing system dependencies and
|
|
the Linux x64 sysroot.
|
|
- Cache Cargo registry, Git checkouts, target artifacts, Rust toolchains, and
|
|
cross-SDK caches with keys including the lockfile, Rust version, target,
|
|
feature set, distro version, and relevant build scripts.
|
|
- Avoid concurrent memory-heavy jobs on one physical host even if several
|
|
logical runners are registered there.
|
|
|
|
Prefer a slow clean build over a fast cache that can mix architectures or stale
|
|
native libraries. Keep caches disabled until their keys and invalidation rules
|
|
are demonstrably correct.
|
|
|
|
## Validation depth
|
|
|
|
Cross-built programs cannot be fully exercised on the ARM64 host, but artifacts
|
|
should still receive structural checks:
|
|
|
|
- ELF: inspect architecture, `DT_NEEDED`, rpath, and unexpected build-host paths
|
|
with `file` and `readelf`.
|
|
- Mach-O: inspect architectures, load commands, bundle layout, plist, signatures,
|
|
and DMG contents with parsers that run on Linux.
|
|
- PE: inspect machine type and imports with LLVM or MinGW object tools.
|
|
- Archives: extract into temporary directories, compare the manifest, and check
|
|
executable modes and licenses.
|
|
|
|
Then smoke-test on real target systems. QEMU or Wine can catch basic loader
|
|
failures but is not authoritative for GTK/WebKit behavior, macOS signing and
|
|
Gatekeeper, or Windows GPU/DirectML behavior.
|
|
|
|
## References
|
|
|
|
- [Gitea Actions](https://docs.gitea.com/usage/actions)
|
|
- [Gitea Actions design and runner labels](https://docs.gitea.com/usage/actions/design)
|
|
- [Gitea Actions variables](https://docs.gitea.com/usage/actions/actions-variables)
|
|
- [Gitea API](https://docs.gitea.com/api/1.24/)
|
|
- [cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild)
|
|
- [cargo-xwin](https://github.com/rust-cross/cargo-xwin)
|
|
|
|
Treat the checked-in workflow and Rust helper as the executable source of truth.
|
|
This document explains why their unusual-looking pieces exist so that future
|
|
agents do not remove them as apparent complexity and rediscover the same target,
|
|
linker, packaging, and release-state failures.
|