Skip to content

Release v0.0.0-beta.5 #4

Release v0.0.0-beta.5

Release v0.0.0-beta.5 #4

Workflow file for this run

name: Release
run-name: Release ${{ github.ref_name }}
on:
push:
tags:
- "v*"
concurrency:
group: release
cancel-in-progress: false
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
jobs:
# ── Validate tag ─────────────────────────────────────────────────────────────
validate-version:
name: Validate Version Tag
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
is_full_release: ${{ steps.version.outputs.is_full_release }}
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Validate tag against Cargo.toml
id: version
run: |
TAG="${GITHUB_REF_NAME}"
TAG_VERSION="${TAG#v}"
CARGO_VERSION=$(cargo metadata --no-deps --format-version=1 \
| jq -r '.packages[] | select(.name == "nodedb-types") | .version')
CARGO_BASE=$(echo "$CARGO_VERSION" | grep -oP '^\d+\.\d+\.\d+')
echo "Tag version: $TAG_VERSION"
echo "Cargo.toml version: $CARGO_VERSION"
echo "Cargo.toml base: $CARGO_BASE"
if [[ ! "$TAG_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+)(-[a-zA-Z]+\.[0-9]+)?$ ]]; then
echo "::error::Invalid tag format '$TAG'. Expected: vX.Y.Z or vX.Y.Z-label.N"
exit 1
fi
TAG_BASE="${BASH_REMATCH[1]}"
if [[ "$TAG_BASE" != "$CARGO_BASE" ]]; then
echo "::error::Base version mismatch! Tag '$TAG_BASE' != Cargo.toml '$CARGO_BASE'"
exit 1
fi
# Full release = no hyphen suffix (v0.0.1, not v0.0.1-beta.1)
if [[ "$TAG_VERSION" == *-* ]]; then
echo "is_full_release=false" >> "$GITHUB_OUTPUT"
else
echo "is_full_release=true" >> "$GITHUB_OUTPUT"
fi
echo "version=$TAG_VERSION" >> "$GITHUB_OUTPUT"
# ── CI gate ──────────────────────────────────────────────────────────────────
ci:
name: CI Gate
needs: validate-version
uses: ./.github/workflows/ci.yml
# ── Publish crates to crates.io ──────────────────────────────────────────────
# Requires secret: CARGO_REGISTRY_TOKEN
# All 4 tiers run in a single job with is_published checks and wait_for polling
# so re-running a failed publish never double-publishes an already-indexed crate.
publish-crates:
name: Publish to crates.io
needs: [validate-version, ci]
runs-on: ubuntu-latest
environment: crates.io
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install system deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
cmake clang libclang-dev pkg-config protobuf-compiler perl \
libcurl4-openssl-dev libsasl2-dev
- name: Set version from tag
run: |
VERSION="${{ needs.validate-version.outputs.version }}"
CURRENT=$(cargo metadata --no-deps --format-version=1 \
| jq -r '.packages[] | select(.name == "nodedb-types") | .version')
if [[ "$VERSION" != "$CURRENT" ]]; then
sed -i "0,/^version = \".*\"/s//version = \"$VERSION\"/" Cargo.toml
# For pre-release versions, pin internal dep requirements so semver
# "0.0.0" doesn't fail to match "0.0.0-beta.1"
if [[ "$VERSION" == *-* ]]; then
sed -i -E 's/(nodedb-(codec|bridge|wal|mem|crdt|raft|fts|types|vector|graph|spatial|strict|client|columnar|cluster|query|sql) = \{ [^}]*version = )"[^"]*"/\1"='"$VERSION"'"/' Cargo.toml
echo "Updated internal dep versions to =$VERSION"
fi
echo "Updated workspace version: $CURRENT -> $VERSION"
else
echo "Version already matches, no change needed"
fi
- name: Publish crates
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
# Tier 1: no internal NodeDB dependencies
TIER1="nodedb-codec nodedb-bridge nodedb-wal nodedb-mem nodedb-crdt nodedb-raft nodedb-fts"
# Tier 2: depends on tier 1 only
TIER2="nodedb-types"
# Tier 3: depends on tier 1-2
TIER3="nodedb-vector nodedb-graph nodedb-spatial nodedb-strict nodedb-client nodedb-sql nodedb-columnar nodedb-cluster"
# Tier 4: depends on tier 3 (nodedb-spatial)
TIER4="nodedb-query"
is_published() {
curl -sf \
-H "User-Agent: nodedb-ci (github.com/NodeDB-Lab/nodedb)" \
"https://crates.io/api/v1/crates/$1/$2" > /dev/null 2>&1
}
wait_for() {
local crate="$1" version="$2"
echo -n " Waiting for $crate@$version..."
for i in $(seq 1 30); do
if is_published "$crate" "$version"; then
echo " ready"
return 0
fi
sleep 5
done
echo " timed out!"
return 1
}
publish_tier() {
local tier_name="$1"; shift
local crates=("$@")
local need_wait=()
echo "::group::Tier: $tier_name"
for crate in "${crates[@]}"; do
VERSION=$(cargo metadata --no-deps --format-version=1 \
| jq -r --arg name "$crate" '.packages[] | select(.name == $name) | .version')
if is_published "$crate" "$VERSION"; then
echo " $crate@$VERSION already published — skipping"
else
echo " Publishing $crate@$VERSION..."
cargo publish -p "$crate" --allow-dirty
need_wait+=("$crate:$VERSION")
fi
done
for entry in "${need_wait[@]}"; do
wait_for "${entry%%:*}" "${entry##*:}"
done
echo "::endgroup::"
}
publish_tier "1 (no internal deps)" $TIER1
publish_tier "2 (depends on tier 1)" $TIER2
publish_tier "3 (depends on tier 2)" $TIER3
publish_tier "4 (depends on tier 3)" $TIER4
# ── Build nodedb server binary (Linux only — requires io_uring) ──────────────
build-server:
name: Build server (${{ matrix.label }})
needs: [validate-version, ci]
runs-on: ${{ matrix.runs-on }}
strategy:
fail-fast: false
matrix:
include:
- runs-on: ubuntu-latest
target: x86_64-unknown-linux-gnu
label: linux-x64
- runs-on: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
label: linux-arm64
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
prefix-key: server-${{ matrix.target }}
- name: Install system deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
cmake clang libclang-dev pkg-config protobuf-compiler perl \
libcurl4-openssl-dev libsasl2-dev
- name: Set version from tag
run: |
VERSION="${{ needs.validate-version.outputs.version }}"
CURRENT=$(cargo metadata --no-deps --format-version=1 \
| jq -r '.packages[] | select(.name == "nodedb-types") | .version')
if [[ "$VERSION" != "$CURRENT" ]]; then
perl -i -pe "if (!\$done && /^version = \"/) { s/^version = \".*\"/version = \"$VERSION\"/; \$done=1 }" Cargo.toml
# For pre-release versions, pin internal dep requirements so semver
# "0" doesn't fail to match "0.0.0-beta.3"
if [[ "$VERSION" == *-* ]]; then
sed -i -E 's/(nodedb-(codec|bridge|wal|mem|crdt|raft|fts|types|vector|graph|spatial|strict|client|columnar|cluster|query|sql) = \{ [^}]*version = )"[^"]*"/\1"='"$VERSION"'"/' Cargo.toml
echo "Updated internal dep versions to =$VERSION"
fi
echo "Updated workspace version: $CURRENT -> $VERSION"
fi
- name: Build server binary
run: cargo build -p nodedb --release --target ${{ matrix.target }}
- name: Package
run: |
cd target/${{ matrix.target }}/release
chmod +x nodedb
tar czf nodedb-${{ needs.validate-version.outputs.version }}-${{ matrix.label }}.tar.gz nodedb
ls -lh nodedb-*.tar.gz
- uses: actions/upload-artifact@v7
with:
name: server-${{ matrix.label }}
path: target/${{ matrix.target }}/release/nodedb-*.tar.gz
retention-days: 1
# ── Create GitHub Release ─────────────────────────────────────────────────────
github-release:
name: Create GitHub Release
needs: [validate-version, publish-crates, build-server]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- name: Download binary artifacts
uses: actions/download-artifact@v8
with:
path: ./artifacts
pattern: "server-*"
merge-multiple: true
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.validate-version.outputs.version }}
name: NodeDB ${{ needs.validate-version.outputs.version }}
generate_release_notes: true
draft: false
prerelease: ${{ contains(needs.validate-version.outputs.version, '-') }}
files: artifacts/*
fail_on_unmatched_files: true
# ── Build and push Docker image (full releases only) ──────────────────────────
# Uses native runners per arch (no QEMU), then merges into a multi-arch manifest.
docker-build:
name: Docker build (${{ matrix.label }})
needs: [validate-version, ci]
if: needs.validate-version.outputs.is_full_release == 'true'
runs-on: ${{ matrix.runs-on }}
strategy:
fail-fast: false
matrix:
include:
- runs-on: ubuntu-latest
platform: linux/amd64
label: amd64
- runs-on: ubuntu-24.04-arm
platform: linux/arm64
label: arm64
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push platform image
uses: docker/build-push-action@v7
with:
context: .
platforms: ${{ matrix.platform }}
push: true
tags: farhansyah/nodedb:${{ needs.validate-version.outputs.version }}-${{ matrix.label }}
cache-from: type=gha,scope=${{ matrix.label }}
cache-to: type=gha,scope=${{ matrix.label }},mode=max
docker-manifest:
name: Docker manifest
needs: [validate-version, docker-build]
if: needs.validate-version.outputs.is_full_release == 'true'
runs-on: ubuntu-latest
steps:
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create and push multi-arch manifest
env:
VERSION: ${{ needs.validate-version.outputs.version }}
run: |
docker buildx imagetools create \
--tag farhansyah/nodedb:${VERSION} \
--tag farhansyah/nodedb:latest \
farhansyah/nodedb:${VERSION}-amd64 \
farhansyah/nodedb:${VERSION}-arm64