chore(release): rust v0.4.9 #9
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: Build, Test, and Deploy (Rust) | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - staging | |
| - trying | |
| - rust/dev | |
| - rust/main | |
| tags: "**" | |
| pull_request: | |
| branches: | |
| - rust/dev | |
| - rust/main | |
| - "feature/**" | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # Enable AES/SSE2 CPU features for gxhash dependency (required by PathMap crate) | |
| # GitHub Actions runners support these features on x86_64 | |
| RUSTFLAGS: "-C target-feature=+aes,+sse2" | |
| jobs: | |
| # Build and save artifacts for next jobs. | |
| # Skip release commits on branch pushes — the tag push handles the release build. | |
| build_base: | |
| name: Build Base | |
| runs-on: ubuntu-latest | |
| if: >- | |
| !startsWith(github.event.head_commit.message, 'chore(release)') | |
| || startsWith(github.ref, 'refs/tags/') | |
| outputs: | |
| VERSION: ${{ env.VERSION }} | |
| BRANCH: ${{ env.BRANCH }} | |
| DEV_LATEST_TAG: ${{ env.DEV_LATEST_TAG }} | |
| steps: | |
| - name: Clone Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Install APT Dependencies | |
| shell: bash -ex {0} | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y $(cat .github/apt-dependencies.txt) | |
| - name: Tool versions | |
| shell: bash -ex {0} | |
| run: | | |
| python --version | |
| pip --version | |
| rustc --version | |
| cargo --version | |
| - name: Initialize Environment | |
| shell: bash -exu -o pipefail {0} | |
| run: | | |
| git fetch origin | |
| git fetch origin --tags --force | |
| # Version from Git repository (tag-commit) | |
| VERSION="$(git describe --tags --always)" | |
| # Strip implementation prefix for Docker-friendly version tags | |
| VERSION="${VERSION#rust-}" | |
| echo "VERSION=$VERSION" >> $GITHUB_ENV | |
| # Find latest tag on rust/dev branch, with fallback | |
| if git ls-remote --heads origin rust/dev 2>/dev/null | grep -q "refs/heads/rust/dev"; then | |
| DEV_LATEST_TAG="$(git describe --tags --abbrev=0 origin/rust/dev 2>/dev/null || echo 'unknown')" | |
| else | |
| DEV_LATEST_TAG="unknown" | |
| fi | |
| echo "DEV_LATEST_TAG=$DEV_LATEST_TAG" >> $GITHUB_ENV | |
| # Find related HEAD branch | |
| BRANCH="" | |
| if [[ $GITHUB_REF =~ ^refs/tags/ ]]; then | |
| # For tag pushes, determine release type from tag name | |
| # patch .0 = main release, patch > 0 = dev release | |
| TAG_NAME="${GITHUB_REF#refs/tags/}" | |
| TAG_VERSION="${TAG_NAME#rust-v}" | |
| PATCH=$(echo "$TAG_VERSION" | cut -d. -f3) | |
| if [ "$PATCH" = "0" ]; then | |
| BRANCH="rust/main" | |
| else | |
| BRANCH="rust/dev" | |
| fi | |
| elif [[ $GITHUB_REF =~ ^refs/heads/ ]]; then | |
| BRANCH=${GITHUB_REF#refs/*/} | |
| else | |
| BRANCH=$GITHUB_HEAD_REF | |
| fi | |
| echo "BRANCH=$BRANCH" >> $GITHUB_ENV | |
| - name: Pack Working Tree | |
| shell: bash -ex {0} | |
| run: tar -H posix -czf /tmp/f1r3fly-worktree.tar.gz . | |
| - name: Save Working Tree | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: f1r3fly-worktree | |
| path: /tmp/f1r3fly-worktree.tar.gz | |
| required_rust_unit_tests: | |
| name: Required Unit Tests (Rust) | |
| needs: build_base | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| tests: | |
| - crypto | |
| - rholang | |
| - rspace++ | |
| - shared | |
| - models | |
| - casper | |
| - node | |
| - block-storage | |
| - comm | |
| - graphz | |
| steps: | |
| - name: Clone Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| - name: Cache Rust dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Install APT Dependencies | |
| shell: bash -ex {0} | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y $(cat .github/apt-dependencies.txt) | |
| - name: Load Working Tree | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: f1r3fly-worktree | |
| path: /tmp | |
| - name: Restore Working Tree | |
| shell: bash -ex {0} | |
| run: tar -H posix -xzf /tmp/f1r3fly-worktree.tar.gz | |
| - name: Run Unit Tests | |
| shell: bash -ex {0} | |
| env: | |
| RUSTFLAGS: "-C target-feature=+aes,+sse2 -D warnings" | |
| RUST_BACKTRACE: "1" | |
| run: | | |
| mkdir -p /tmp/test-logs | |
| pushd ${{ matrix.tests }} | |
| cargo test --release 2>&1 | tee /tmp/test-logs/${{ matrix.tests }}-test-output.txt | |
| TEST_EXIT_CODE=${PIPESTATUS[0]} | |
| popd | |
| exit $TEST_EXIT_CODE | |
| - name: Upload Test Logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-logs-${{ matrix.tests }} | |
| path: /tmp/test-logs/ | |
| retention-days: 7 | |
| # Build Rust Docker image natively on each arch using GitHub-hosted runners. | |
| build_rust_docker_image: | |
| name: Build Rust Docker Image (${{ matrix.arch }}) | |
| needs: build_base | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| matrix: | |
| include: | |
| - platform: linux/amd64 | |
| runner: ubuntu-latest | |
| arch: amd64 | |
| - platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| arch: arm64 | |
| steps: | |
| - name: Clone Repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build Docker Image | |
| shell: bash -ex {0} | |
| run: | | |
| docker buildx build \ | |
| --platform ${{ matrix.platform }} \ | |
| --file node/Dockerfile \ | |
| --tag f1r3flyindustries/f1r3fly-rust-node:${{ matrix.arch }} \ | |
| --load \ | |
| . | |
| - name: Export Docker Image | |
| shell: bash -ex {0} | |
| run: | | |
| mkdir -p /tmp/artifacts | |
| git describe --tags --always >/tmp/artifacts/version.txt | |
| docker image save f1r3flyindustries/f1r3fly-rust-node:${{ matrix.arch }} \ | |
| | gzip > /tmp/rust-node-docker.tar.gz | |
| - name: Save Docker Image | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: artifacts-docker-${{ matrix.arch }} | |
| path: /tmp/rust-node-docker.tar.gz | |
| # Integration tests on GitHub-hosted runners, parallelized by test module. | |
| # Each test module gets its own ephemeral runner (2 arches × 16 modules = 32 jobs). | |
| required_rust_integration_tests: | |
| name: Integration Tests (${{ matrix.arch.name }}/${{ matrix.test }}) | |
| needs: build_rust_docker_image | |
| runs-on: ${{ matrix.arch.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: | |
| - { runner: ubuntu-latest, name: amd64 } | |
| - { runner: ubuntu-24.04-arm, name: arm64 } | |
| test: | |
| - test_web_api | |
| - test_wallets | |
| - test_heartbeat | |
| - test_deployment | |
| - test_storage | |
| - test_genesis_ceremony | |
| - test_dag_correctness | |
| - test_finalization | |
| - test_propose | |
| - test_replay_determinism | |
| - test_consensus_health | |
| - test_synchrony_constraint | |
| - test_asymmetric_bonds | |
| - test_bonding_validators | |
| - test_trim_state | |
| steps: | |
| - name: Clone Repository | |
| uses: actions/checkout@v4 | |
| - name: Clone system-integration | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: F1R3FLY-io/system-integration | |
| ref: main | |
| path: system-integration | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.10" | |
| cache: 'pip' | |
| - name: Install Poetry | |
| uses: snok/install-poetry@v1 | |
| with: | |
| version: 1.8.5 | |
| virtualenvs-create: true | |
| virtualenvs-in-project: true | |
| - name: Load Docker Image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: artifacts-docker-${{ matrix.arch.name }} | |
| path: /tmp | |
| - name: Import Docker Image | |
| shell: bash -ex {0} | |
| run: | | |
| zcat /tmp/rust-node-docker.tar.gz | docker image load | |
| docker tag f1r3flyindustries/f1r3fly-rust-node:${{ matrix.arch.name }} f1r3flyindustries/f1r3fly-rust-node | |
| - name: Install system-integration dependencies | |
| shell: bash -ex {0} | |
| run: | | |
| cd system-integration | |
| poetry lock --no-update | |
| poetry install --with integration | |
| - name: Ensure test ports are free | |
| shell: bash {0} | |
| run: | | |
| for port in $(seq 40400 40545); do | |
| fuser -k $port/tcp 2>/dev/null || true | |
| done | |
| - name: Run Integration Tests | |
| shell: bash -ex {0} | |
| env: | |
| DEFAULT_IMAGE: f1r3flyindustries/f1r3fly-rust-node | |
| run: | | |
| cd system-integration | |
| SCALE_FLAG="" | |
| if [ "${{ matrix.arch.name }}" = "arm64" ]; then | |
| SCALE_FLAG="--timeout-scale=1.5" | |
| fi | |
| DESELECT_FLAG="" | |
| if [ "${{ matrix.test }}" = "test_replay_determinism" ]; then | |
| # Skip test_network_recovers_from_slow_deploy — see f1r3node#463 | |
| DESELECT_FLAG="--deselect integration-tests/test/test_replay_determinism.py::test_network_recovers_from_slow_deploy" | |
| fi | |
| poetry run pytest integration-tests/test/${{ matrix.test }}.py -v --tb=short --log-cli-level=WARNING \ | |
| --startup-timeout=600 --command-timeout=300 --timeout=600 $SCALE_FLAG $DESELECT_FLAG | |
| - name: Upload Integration Test Logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: integration-logs-${{ matrix.arch.name }}-${{ matrix.test }} | |
| path: system-integration/integration-tests/ | |
| retention-days: 7 | |
| - name: Upload Docker Logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-logs-${{ matrix.arch.name }}-${{ matrix.test }} | |
| path: /tmp/*.log | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| # release_* jobs make built artifacts available to public and run only on new | |
| # tags or pushes to "staging" or "trying" branches used by Bors (bors r+ and bors try). | |
| # These jobs require secrets! See "env" variables and "Secrets" page in GitHub repository | |
| # settings. Release destinations differ slightly depending on the event that | |
| # triggered the job (tag or branch push). See "Publish ..." steps for details. | |
| # VERSION and BRANCH are used from "build_base" job outputs as a new way to share | |
| # data between jobs and steps. Legacy "version" via artifact file is still used. | |
| # Upload Rust Docker image to Docker Hub (multi-arch manifest). | |
| release_rust_docker_image: | |
| name: Release Rust Docker Image | |
| needs: | |
| - required_rust_unit_tests | |
| - required_rust_integration_tests | |
| - build_rust_docker_image | |
| - build_base | |
| if: >- | |
| github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/') || | |
| github.ref == 'refs/heads/trying' || | |
| github.ref == 'refs/heads/staging' || | |
| github.ref == 'refs/heads/rust/main' || | |
| github.ref == 'refs/heads/rust/dev') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Load AMD64 Docker Image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: artifacts-docker-amd64 | |
| path: /tmp/amd64 | |
| - name: Import AMD64 Docker Image | |
| shell: bash -ex {0} | |
| run: zcat /tmp/amd64/rust-node-docker.tar.gz | docker image load | |
| - name: Load ARM64 Docker Image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: artifacts-docker-arm64 | |
| path: /tmp/arm64 | |
| - name: Import ARM64 Docker Image | |
| shell: bash -ex {0} | |
| run: zcat /tmp/arm64/rust-node-docker.tar.gz | docker image load | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Publish Docker Image | |
| env: | |
| VERSION: ${{ needs.build_base.outputs.VERSION }} | |
| BRANCH: ${{ needs.build_base.outputs.BRANCH }} | |
| DEV_LATEST_TAG: ${{ needs.build_base.outputs.DEV_LATEST_TAG }} | |
| shell: bash -exu -o pipefail {0} | |
| run: | | |
| DOCKER_IMAGE_NAME="f1r3flyindustries/f1r3fly-rust-node" | |
| SOURCE_AMD64="${DOCKER_IMAGE_NAME}:amd64" | |
| SOURCE_ARM64="${DOCKER_IMAGE_NAME}:arm64" | |
| SUFFIX="" | |
| CI_RUN="" | |
| TAG_PREFIX="" | |
| IS_TAG_PUSH="false" | |
| if [[ $GITHUB_REF =~ ^refs/tags/ ]]; then | |
| IS_TAG_PUSH="true" | |
| fi | |
| # Add suffix if trying or merging | |
| if [[ "$BRANCH" =~ ^(trying|staging)$ ]]; then | |
| SUFFIX="-$BRANCH" | |
| # Add CI run number if trying | |
| if [[ $BRANCH =~ ^trying$ ]]; then | |
| CI_RUN="-ci-$GITHUB_RUN_NUMBER" | |
| fi | |
| elif [[ "$BRANCH" == "rust/dev" && "$IS_TAG_PUSH" == "false" ]]; then | |
| # Only prefix dev- for branch pushes (commit hashes), not tag releases | |
| TAG_PREFIX="dev-" | |
| fi | |
| set -x | |
| IMAGE_NAME="$DOCKER_IMAGE_NAME$SUFFIX" | |
| IMG_VERSION_RAW="$IMAGE_NAME:${TAG_PREFIX}$VERSION$CI_RUN" | |
| # Replace unsupported character `+` | |
| IMG_VERSION="${IMG_VERSION_RAW//[+]/__}" | |
| if [[ "$BRANCH" == "rust/dev" ]]; then | |
| IMG_LATEST="$IMAGE_NAME:dev" | |
| else | |
| IMG_LATEST="$IMAGE_NAME:latest" | |
| fi | |
| # Tag and push arch-specific images | |
| docker tag $SOURCE_AMD64 $IMG_VERSION-amd64 | |
| docker tag $SOURCE_ARM64 $IMG_VERSION-arm64 | |
| docker push $IMG_VERSION-amd64 | |
| docker push $IMG_VERSION-arm64 | |
| docker tag $SOURCE_AMD64 $IMG_LATEST-amd64 | |
| docker tag $SOURCE_ARM64 $IMG_LATEST-arm64 | |
| docker push $IMG_LATEST-amd64 | |
| docker push $IMG_LATEST-arm64 | |
| # Create and push multi-arch manifests | |
| docker manifest create $IMG_VERSION --amend $IMG_VERSION-amd64 $IMG_VERSION-arm64 | |
| docker manifest push $IMG_VERSION | |
| docker manifest create $IMG_LATEST --amend $IMG_LATEST-amd64 $IMG_LATEST-arm64 | |
| docker manifest push $IMG_LATEST | |
| # GitHub (create) release and packages | |
| release_packages: | |
| name: Release Packages | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/rust-v') | |
| permissions: | |
| contents: write # Required for creating/updating releases | |
| needs: | |
| - required_rust_unit_tests | |
| - required_rust_integration_tests | |
| - build_rust_docker_image | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout (for CHANGELOG.md) | |
| uses: actions/checkout@v4 | |
| - name: Load Docker artifact (amd64) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: artifacts-docker-amd64 | |
| path: /tmp/artifacts | |
| - name: Extract node binary from Docker image | |
| run: | | |
| zcat /tmp/artifacts/rust-node-docker.tar.gz | docker image load | |
| CONTAINER_ID=$(docker create f1r3flyindustries/f1r3fly-rust-node:amd64) | |
| docker cp "$CONTAINER_ID:/opt/docker/bin/node" /tmp/artifacts/f1r3node-linux-amd64 | |
| docker rm "$CONTAINER_ID" | |
| test -f /tmp/artifacts/f1r3node-linux-amd64 || { echo 'Binary extraction failed'; exit 1; } | |
| - name: Determine release type | |
| id: release_type | |
| run: | | |
| TAG="${GITHUB_REF#refs/tags/rust-v}" | |
| PATCH=$(echo "$TAG" | cut -d. -f3) | |
| if [ "$PATCH" = "0" ]; then | |
| echo "prerelease=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "prerelease=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Extract release notes for this version | |
| run: | | |
| # Extract only the latest version section from CHANGELOG.md | |
| awk '/^## \[/{if(found) exit; found=1} found' CHANGELOG.md > RELEASE_NOTES.md | |
| - name: Update release and release artifacts | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| artifacts: /tmp/artifacts/* | |
| prerelease: ${{ steps.release_type.outputs.prerelease }} | |
| draft: false | |
| allowUpdates: true | |
| omitBodyDuringUpdate: true | |
| bodyFile: RELEASE_NOTES.md | |
| token: ${{ secrets.GITHUB_TOKEN }} |