Release v0.0.0-beta.5 #4
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |