feat: add static musl Linux binary built in Docker and published in CI (FJMCP-4) #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/static-musl-binary-fjmcp-4"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Resolves FJMCP-4.
Adds a fully static
x86_64-unknown-linux-muslbinary (forgejo-mcp-linux-x86_64-static) alongside the existing dynamic glibc Linux and mingw Windows artifacts, so forgejo-mcp runs on any Linux without a matching glibc/OpenSSL. The glibc and Windows artifacts are unchanged.What changed
oci-build/Dockerfile.static: builds the musl release binary and exports it through aFROM scratch AS binarystage (no runtime stage, since a static binary needs no base image). Mirrorsoci-build/Dockerfile: dummy-main dependency-cache prime,sharing=lockedcargo cache mounts,CARGO_BUILD_JOBS/GIT_SHAbuild args.Cargo.toml/Cargo.lock:opensslis declared as a direct dependency with thevendoredfeature, scoped to[target.'cfg(target_env = "musl")'.dependencies]. Cargo feature unification then statically vendors OpenSSL into every transitiveopenssl-sysfor the musl build only, so the glibc and Windows builds keep linking OpenSSL exactly as before.libgit2/libssh2/zliblink statically because building for a non-host target triple makes thepkg-configcrate skip system-library probing and fall back to their bundledcc-compiled sources..forgejo/workflows/build-binary-static.yml: mirrorsbuild-binary.yml(samemain/v*triggers andsrc+Cargo.*+oci-build/**+ own-filepaths); publishes the-staticartifact to Generic Packages.CARGO_BUILD_JOBSdivisor dropped fromnproc/2tonproc/3across all three binary workflows (floored at 1): one push can now run three concurrent binary builds on a single runner, so this keeps combined parallelism nearnprocperCI.md.just build-staticrecipe;README.mdandCLAUDE.mddocument the new artifact and reconcile the prior glibc-not-musl note.Tooling gap
No
rust-builder-muslimage exists in the org builder-image family yet (docker manifest inspect ghcr.io/niceguyit/rust-builder-musl:...returnsmanifest unknown), soDockerfile.staticadds thex86_64-unknown-linux-muslrustup target andmusl-toolsinline. That gap is tracked in FJMCP-5 (filed, not worked around silently) to move the toolchain into arust-builder-muslimage so the inline block can be dropped.Verification
Built locally with
just build-static:file ./output/forgejo-mcp->ELF 64-bit LSB pie executable, x86-64, static-pie linkedldd ./output/forgejo-mcp->statically linkedreadelf -dshows noNEEDED/libssl/libcrypto/libgit2/libssh2entriesinitializerequest over stdio and exits cleanlyjust pre-commit(fmt + clippy + build + test in the glibc image) passes; the glibc build compilesgit2/ssh2-configbut notopenssl-src, confirming the vendored feature stays inactive for glibcTest plan
main,build-binary-static.ymlbuilds the static binary in Docker and publishesforgejo-mcp-linux-x86_64-staticto Generic Packagesbuild-binary.ymlandbuild-binary-windows.ymlstill publishforgejo-mcp-linux-x86_64andforgejo-mcp-windows-x86_64.exeunchangedResolve conflicts from the FJMCP-3 (clap CLI + version self-updater, with build.rs baking FORGEJO_MCP_GIT_HASH / FORGEJO_MCP_BUILD_DATE / FORGEJO_MCP_UPDATE_URL) and FJMCP-6 (Streamable HTTP transport) work that landed on main after this branch was cut. Cargo.toml: keep both the musl-scoped `openssl` vendored dependency and main's `[dev-dependencies]` (tempfile, wiremock). Cargo.lock: keep both `openssl` and `reqwest` under the forgejo-mcp package. build-binary.yml / build-binary-windows.yml: keep main's `build_date` build-meta line and apply this branch's nproc/3 concurrency cap (three binary workflows now share a runner). Integrate the static build with main's build.rs, which this branch predated: oci-build/Dockerfile.static now `COPY build.rs ./` (the crate uses `env!("FORGEJO_MCP_GIT_HASH")` at compile time, so the musl build fails to compile without it) and accepts the FORGEJO_MCP_GIT_HASH / FORGEJO_MCP_BUILD_DATE build args, mirroring oci-build/Dockerfile. build-binary-static.yml now computes build_date, passes both build-meta args, and publishes a `.sha256` sidecar, matching the glibc and Windows workflows and the CLAUDE.md "each published artifact gets a .sha256 sidecar" convention. Verified: `cargo build --release --locked` on glibc (openssl-src is not compiled, so the glibc artifact is unchanged) and the full musl Docker build (`file` -> static-pie, `ldd` -> statically linked, `readelf -d` -> no NEEDED/libssl/libgit2/libssh2, `--version` bakes the real git hash). #FJMCP-4 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Resolved the merge conflicts against current
main(merge commit, no rebase/force-push).Conflicts were caused by FJMCP-3 (clap CLI +
versionself-updater, includingbuild.rs) and FJMCP-6 (Streamable HTTP transport) landing onmainafter this branch was cut. Resolution:Cargo.toml: kept both the musl-scopedopenssl(vendored) dependency and main's[dev-dependencies](tempfile, wiremock).Cargo.lock: kept bothopensslandreqwest.build-binary.yml/build-binary-windows.yml: kept main'sbuild_datebuild-meta line and applied this branch'snproc/3concurrency cap.build.rs(this branch predated it):oci-build/Dockerfile.staticnowCOPY build.rs ./(the crate usesenv!("FORGEJO_MCP_GIT_HASH")at compile time, so the musl build will not compile without it) and accepts theFORGEJO_MCP_GIT_HASH/FORGEJO_MCP_BUILD_DATEbuild args, mirroringoci-build/Dockerfile.build-binary-static.ymlnow computesbuild_date, passes both build-meta args, and publishes a.sha256sidecar, matching the glibc/Windows workflows and the CLAUDE.md "each published artifact gets a.sha256sidecar" convention.Verification after the merge:
just pre-commitpasses (fmt + clippy + build + 36 tests in the glibc image); the host glibc build does not compileopenssl-src(glibc artifact unchanged); the full musl Docker build produces afile-> static-pie,ldd-> statically linked binary with noNEEDED/libssl/libgit2/libssh2entries, and--versionbakes the real git hash.