Skip to content

v3.4.1

v3.4.1 #89

name: Build and publish release packages
permissions:
contents: write
packages: write
on:
release:
# 'published' is triggered when publishing draft release, 'created' is not
types: [published]
env:
# phase34 / #320 — CI starts cold, so incremental compilation
# only adds artifacts and slows the build (Rust perf-team:
# https://kobzol.github.io/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times.html).
CARGO_INCREMENTAL: "0"
jobs:
# Build dashboard first - shared by all jobs
build-dashboard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm
- name: Set version from release tag
run: echo "VITE_APP_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Build dashboard
run: |
cd dashboard
pnpm install --frozen-lockfile
pnpm build
- name: Upload dashboard artifacts
uses: actions/upload-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
retention-days: 1
# Separated Linux builds - each runs independently
# If one fails, others continue
build-linux-x86_64-gnu:
needs: build-dashboard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Download dashboard artifacts
uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y gcc-multilib clang cmake protobuf-compiler libssl-dev pkg-config
rustup default stable
rustup update
- name: Override leaked Windows OPENSSL_DIR (point at system libssl)
# The ubuntu-latest GH-hosted runner image leaks
# OPENSSL_DIR=C:/Program Files/OpenSSL-Win64 (and
# OPENSSL_NO_VENDOR=1) into Linux jobs, which together make
# openssl-sys's build script panic with "OpenSSL include
# directory does not exist" before the vendored-openssl
# feature can take over. Point at the system libssl-dev
# install instead -- same fix applied to rust.yml in 7fad253d.
run: |
echo "OPENSSL_DIR=/usr" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=/usr/include" >> $GITHUB_ENV
echo "OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV
shell: bash
- name: Build and publish vectorizer
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer
target: x86_64-unknown-linux-gnu
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish vectorizer-cli
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer-cli
target: x86_64-unknown-linux-gnu
token: ${{ secrets.GITHUB_TOKEN }}
# Linux glibc binaries for Docker: amd64 em ubuntu-latest, arm64 em ubuntu-22.04-arm (glibc 2.35 = compatível com distroless Debian 12 / glibc 2.36)
build-linux-gnu-for-docker:
needs: build-dashboard
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
lib_dir: x86_64-linux-gnu
- arch: arm64
# ubuntu-22.04-arm ships glibc 2.35; the prebuilt
# ONNX Runtime that `ort-sys` pulls in references
# `__isoc23_strtol` (glibc 2.38+) and `__cxa_call_terminate`
# (newer libstdc++), so the link step fails with
# `undefined reference`. ubuntu-24.04-arm has glibc 2.39
# plus a libstdc++ that resolves both symbols.
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
lib_dir: aarch64-linux-gnu
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
- name: Download dashboard artifacts
uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
- name: Install dependencies
env:
MATRIX_TARGET: ${{ matrix.target }}
run: |
sudo apt-get update
sudo apt-get install -y gcc clang cmake protobuf-compiler libssl-dev pkg-config
rustup default stable
rustup update
rustup target add "$MATRIX_TARGET"
- name: Override leaked Windows OPENSSL_DIR (point at system libssl)
# See comment on the same step in build-linux-x86_64-gnu above.
# `lib_dir` is matrix-driven so the arm64 runner finds
# /usr/lib/aarch64-linux-gnu instead of the amd64 path.
env:
MATRIX_LIB_DIR: ${{ matrix.lib_dir }}
run: |
echo "OPENSSL_DIR=/usr" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=/usr/include" >> $GITHUB_ENV
echo "OPENSSL_LIB_DIR=/usr/lib/$MATRIX_LIB_DIR" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/usr/lib/$MATRIX_LIB_DIR/pkgconfig" >> $GITHUB_ENV
shell: bash
- name: Build (gnu)
# Drop the bogus `--features vendored-openssl` -- vectorizer-server
# has no such feature, and now that the leaked OPENSSL_DIR is
# overridden we link against the system libssl-dev installed
# above. Drop `--no-default-features` since vectorizer-server
# has no relevant defaults (only the opt-in `s2s-tests`).
env:
MATRIX_TARGET: ${{ matrix.target }}
run: cargo build --release --target "$MATRIX_TARGET" --bin vectorizer
- name: Prepare binary for artifact
run: |
mkdir -p binaries/linux-${{ matrix.arch }}
cp target/${{ matrix.target }}/release/vectorizer binaries/linux-${{ matrix.arch }}/
chmod +x binaries/linux-${{ matrix.arch }}/vectorizer
- name: Upload linux binary for Docker
uses: actions/upload-artifact@v4
with:
name: linux-binaries-${{ matrix.arch }}
path: binaries/linux-${{ matrix.arch }}
retention-days: 1
# build-linux-aarch64-musl: removed in v3.0.8.
#
# The aarch64 musl artifact has been a perennial release blocker:
# `cross` auto-forwards the GH-runner's leaked Win64
# OPENSSL_DIR=C:/Program Files/OpenSSL-Win64 into its container even
# after the workflow `unset`s it and Cross.toml passthrough is empty,
# so openssl-sys panics before vendored-openssl can run. Fixing it
# properly means either patching the cross image (Dockerfile.aarch64-
# unknown-linux-musl above) to ENV-clear OPENSSL_DIR, or replacing
# cross with a native ubuntu-24.04-arm + musl-tools build. Both are
# tracked separately; the rest of the matrix (amd64 GNU + Docker
# multi-arch + Apple Silicon plain + Apple Silicon Metal + Windows)
# ships every other release target.
build-mac-binaries:
needs: build-dashboard
strategy:
matrix:
# x86_64-apple-darwin used to live here; dropped because
# `ort-sys` (transitive via the `fastembed` default feature)
# ships no prebuilt for Intel Mac and the workflow has no
# `ORT_STRATEGY=compile` plumbing. Apple Silicon is the only
# supported macOS target now -- the sibling
# `build-mac-metal-binaries` job covers the same target with
# Metal GPU support.
include:
- target: aarch64-apple-darwin
os: macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Download dashboard artifacts
uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
- name: Install dependencies
run: |
brew update-reset
brew install gcc cmake protobuf
rustup default stable
rustup update
rustup show
cargo -Vv
- name: Build and publish vectorizer
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer
target: ${{ matrix.target }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish vectorizer-cli
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer-cli
target: ${{ matrix.target }}
token: ${{ secrets.GITHUB_TOKEN }}
build-mac-metal-binaries:
needs: build-dashboard
name: Build macOS ARM with Metal GPU support
strategy:
matrix:
include:
- target: aarch64-apple-darwin
os: macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Download dashboard artifacts
uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
- name: Install dependencies
run: |
brew update-reset
brew install gcc cmake protobuf
rustup default stable
rustup update
rustup target add ${{ matrix.target }}
rustup show
cargo -Vv
# Tag separado para Mac com Metal: asset nomeado vectorizer-*-metal (resto = manifesto automático)
- name: Build vectorizer with Metal support
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer
target: ${{ matrix.target }}
features: hive-gpu
archive: vectorizer-${{ matrix.target }}-metal
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build vectorizer-cli with Metal support
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer-cli
target: ${{ matrix.target }}
features: hive-gpu
archive: vectorizer-cli-${{ matrix.target }}-metal
token: ${{ secrets.GITHUB_TOKEN }}
build-windows-binaries:
needs: build-dashboard
strategy:
matrix:
include:
- os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- name: Install minimal stable
uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@v7
- name: Download dashboard artifacts
uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
- uses: Swatinem/rust-cache@v2
- name: Install protoc
shell: pwsh
run: |
# Chocolatey mirror returns flaky 503s — retry up to 3 times.
$ok = $false
for ($i = 1; $i -le 3; $i++) {
choco install protoc -y --no-progress
if ($LASTEXITCODE -eq 0) { $ok = $true; break }
Write-Host "choco install protoc attempt $i failed; sleeping 15s"
Start-Sleep -Seconds 15
}
if (-not $ok) { Write-Error "protoc install failed after 3 attempts"; exit 1 }
- name: Build
run: cargo build --release
- name: Build and publish vectorizer
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish vectorizer-cli
uses: taiki-e/upload-rust-binary-action@v1
with:
bin: vectorizer-cli
token: ${{ secrets.GITHUB_TOKEN }}
# Docker: per-arch single-platform image build on a NATIVE runner (no
# QEMU emulation), pushed by digest. The sibling
# `publish-docker-manifest` job downstream stitches both digests into a
# single multi-arch manifest list and pushes the human-readable tags
# to both ghcr.io (always) and `hivehub/vectorizer` on Docker Hub
# (gated on `DOCKERHUB_USERNAME` + `DOCKERHUB_TOKEN` repo secrets).
#
# See `docs/development/docker-builds.md` for the full pipeline
# diagram and the rationale (phase10_optimize-docker-build-time).
publish-docker:
needs:
- build-dashboard
- build-linux-gnu-for-docker
- build-linux-x86_64-gnu
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
platform: linux/amd64
- arch: arm64
runner: ubuntu-24.04-arm
platform: linux/arm64
runs-on: ${{ matrix.runner }}
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
permissions:
contents: read
packages: write
id-token: write # required for SLSA provenance attestation
steps:
- uses: actions/checkout@v7
- name: Download dashboard artifacts
uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: dashboard/dist
- name: Download Linux binary (${{ matrix.arch }})
uses: actions/download-artifact@v4
with:
name: linux-binaries-${{ matrix.arch }}
path: binaries/linux-${{ matrix.arch }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: env.DOCKERHUB_USERNAME != ''
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push by digest (${{ matrix.platform }})
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile.artifacts
push: true
platforms: ${{ matrix.platform }}
# Push by digest (no tag) — the manifest job below assembles
# the human-readable tags from these digests.
outputs: type=image,name=ghcr.io/${{ github.repository }},push-by-digest=true,name-canonical=true,push=true
provenance: mode=max
sbom: true
build-args: |
GIT_COMMIT_ID=${{ steps.version.outputs.VERSION }}
BUILD_DATE=${{ github.event.release.published_at }}
# GHA cache scoped per-arch so amd64 and arm64 don't fight
# for the same cache key.
cache-from: type=gha,scope=docker-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=docker-${{ matrix.arch }}
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: docker-digest-${{ matrix.arch }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
# Manifest list assembly: stitches the per-arch digests pushed by the
# matrix job above into a single multi-arch manifest list, tags it
# on both ghcr.io and (when secrets are configured) Docker Hub. Final
# step runs `docker scout policy` against the published manifest as a
# supply-chain gate — fails the workflow on policy regression.
publish-docker-manifest:
needs: publish-docker
runs-on: ubuntu-latest
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: docker-digest-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: env.DOCKERHUB_USERNAME != ''
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker metadata (ghcr.io)
id: meta-ghcr
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Docker metadata (Docker Hub)
id: meta-hub
if: env.DOCKERHUB_USERNAME != ''
uses: docker/metadata-action@v6
with:
images: hivehub/vectorizer
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Create manifest list (ghcr.io)
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta-ghcr.outputs.json }}
- name: Create manifest list (Docker Hub)
if: env.DOCKERHUB_USERNAME != ''
working-directory: /tmp/digests
# Same per-arch digests as the ghcr.io step — buildx
# imagetools transparently re-tags / cross-pushes the manifest
# list to the Docker Hub repo without re-uploading layers.
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta-hub.outputs.json }}
- name: Inspect manifest list
run: |
PRIMARY=$(jq -cr '.tags[0]' <<< "${{ steps.meta-ghcr.outputs.json }}")
docker buildx imagetools inspect "$PRIMARY"
- name: Resolve published version tag
id: version
# The release tag arrives as `refs/tags/v3.x.y`; strip the `v`
# to match the metadata-action's `{{version}}` output, which is
# what both ghcr.io and Docker Hub were tagged with above.
run: |
ref="${GITHUB_REF#refs/tags/}"
echo "VERSION=${ref#v}" >> "$GITHUB_OUTPUT"
- name: Docker Scout policy gate
if: env.DOCKERHUB_USERNAME != ''
uses: docker/scout-action@v1
with:
command: policy
image: hivehub/vectorizer:${{ steps.version.outputs.VERSION }}
organization: hivehub
platform: linux/amd64
exit-code: true
dockerhub-user: ${{ secrets.DOCKERHUB_USERNAME }}
dockerhub-password: ${{ secrets.DOCKERHUB_TOKEN }}