diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3750c4ede..a56d60898 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -331,7 +331,18 @@ jobs: run: | target="${IMAGE}@${INDEX_DIGEST}" echo "Signing ${target} (signatures -> ${COSIGN_REPOSITORY})" - cosign sign --yes "${target}" + for attempt in 1 2 3; do + if cosign sign --yes "${target}"; then + exit 0 + fi + if [ "$attempt" -eq 3 ]; then + echo "ERROR: cosign signing failed after ${attempt} attempts" >&2 + exit 1 + fi + sleep_for=$((attempt * 10)) + echo "cosign signing failed on attempt ${attempt}; retrying in ${sleep_for}s" >&2 + sleep "$sleep_for" + done promote-latest: # Re-push the :latest tag pointing at the root variant *after* every diff --git a/Cargo.lock b/Cargo.lock index c908ff8b2..c3f159b57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2487,6 +2487,16 @@ dependencies = [ "cc", ] +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libredox" version = "0.1.16" @@ -2926,6 +2936,7 @@ version = "2.0.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133" dependencies = [ + "libloading", "ndarray", "ort-sys", "smallvec", diff --git a/crates/headroom-core/Cargo.toml b/crates/headroom-core/Cargo.toml index 2ec4cead8..a5705ba8a 100644 --- a/crates/headroom-core/Cargo.toml +++ b/crates/headroom-core/Cargo.toml @@ -61,11 +61,6 @@ flate2 = "1" # (release matrix, e2e/wrap, e2e/init, devcontainer, ci.yml) needs # system OpenSSL + perl modules for openssl-src vendored compile. Using # rustls everywhere removes the entire OpenSSL build-deps surface. -fastembed = { version = "5", default-features = false, features = [ - "hf-hub-rustls-tls", - "ort-download-binaries-rustls-tls", - "image-models", -] } # `magika` is Google's ONNX-backed content classifier (Tier 1 of the # Stage-3d ContentRouter detection arch). Bundled standard-model is # loaded once per process via `OnceLock` and shared across calls. The @@ -127,6 +122,23 @@ redis = { version = "0.27", optional = true, default-features = false } # cycling through the proxy crate. Tiny crate (no I/O, just types). http = "1" +[target.'cfg(not(windows))'.dependencies] +fastembed = { version = "5", default-features = false, features = [ + "hf-hub-rustls-tls", + "ort-download-binaries-rustls-tls", + "image-models", +] } + +[target.'cfg(windows)'.dependencies] +# `ort-download-binaries-*` emits DirectML link libs on Windows (`DXCORE`, +# `DXGI`, `D3D12`, `DirectML`). Users installing `headroom-ai[all]` from +# sdist often do not have those SDK libs, so load ORT dynamically instead. +fastembed = { version = "5", default-features = false, features = [ + "hf-hub-rustls-tls", + "ort-load-dynamic", + "image-models", +] } + [features] default = [] # Compile in the Redis CCR backend. Enable for multi-worker deployments diff --git a/tests/test_release_workflows.py b/tests/test_release_workflows.py index b3e18a4cd..ffeb0c394 100644 --- a/tests/test_release_workflows.py +++ b/tests/test_release_workflows.py @@ -187,6 +187,27 @@ def test_fastembed_uses_rustls_features() -> None: assert '"image-models"' in cargo +def test_fastembed_uses_dynamic_ort_on_windows() -> None: + """Windows sdist builds must not link Pyke's DirectML ORT binaries. + + `ort-download-binaries-*` emits DXCORE/DXGI/D3D12/DirectML link libs on + Windows. Those SDK libs are not present on many Python build hosts, so the + Windows target must use ORT dynamic loading instead. + """ + + cargo = (ROOT / "crates" / "headroom-core" / "Cargo.toml").read_text(encoding="utf-8") + assert "[target.'cfg(windows)'.dependencies]" in cargo + windows_section = cargo.split("[target.'cfg(windows)'.dependencies]", 1)[1].split( + "\n[", + 1, + )[0] + windows_dependency_lines = "\n".join( + line for line in windows_section.splitlines() if not line.lstrip().startswith("#") + ) + assert '"ort-load-dynamic"' in windows_section + assert "ort-download-binaries" not in windows_dependency_lines + + def test_dockerfiles_no_longer_install_openssl_devel() -> None: """Once openssl-sys is out of the build tree, every Dockerfile that used to install `openssl-devel` / `libssl-dev` for the Rust