v0.30.0 #589
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: CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| pull_request: | |
| branches: | |
| - main | |
| - develop | |
| release: | |
| types: | |
| - published | |
| workflow_dispatch: | |
| inputs: | |
| test-release: | |
| description: "Simulate a release" | |
| required: false | |
| default: "true" | |
| # Cancel in-progress workflows when a new commit is pushed to the same branch | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| RUSTC_VERSION: 1.90.0 | |
| jobs: | |
| cancel-previous: | |
| name: Cancel Previous Runs | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Cancel Previous Runs | |
| uses: styfle/cancel-workflow-action@0.12.1 | |
| with: | |
| access_token: ${{ github.token }} | |
| extract-version: | |
| name: Extract Version | |
| runs-on: ubuntu-latest | |
| needs: cancel-previous | |
| outputs: | |
| current_version: ${{ steps.extract.outputs.current_version }} | |
| tag: ${{ steps.extract.outputs.tag }} | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Set Up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13" | |
| - name: Extract current version | |
| id: extract | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| current_version=$(python ci/extract_version.py) | |
| echo "current_version=$current_version" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$current_version" >> "$GITHUB_OUTPUT" | |
| detect-version-bump: | |
| name: Detect Version Bump | |
| runs-on: ubuntu-latest | |
| needs: extract-version | |
| outputs: | |
| bumped: ${{ steps.detect.outputs.bumped }} | |
| current_version: ${{ steps.detect.outputs.current_version }} | |
| previous_version: ${{ steps.detect.outputs.previous_version }} | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set Up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13" | |
| - name: Detect version change on main push | |
| id: detect | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| current_version="${{ needs.extract-version.outputs.current_version }}" | |
| previous_version="" | |
| bumped="false" | |
| if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then | |
| before_sha="${{ github.event.before }}" | |
| if [[ -n "$before_sha" && "$before_sha" != "0000000000000000000000000000000000000000" ]]; then | |
| if git cat-file -e "$before_sha:Cargo.toml" 2>/dev/null; then | |
| previous_version=$(git show "$before_sha:Cargo.toml" | python ci/extract_version.py --stdin) | |
| else | |
| echo "Previous commit does not contain Cargo.toml; treating as no bump." | |
| fi | |
| if [[ -z "$previous_version" ]]; then | |
| echo "Could not parse previous Cargo.toml version; treating as no bump." | |
| elif [[ "$current_version" != "$previous_version" ]]; then | |
| bumped="true" | |
| fi | |
| else | |
| echo "No valid before SHA available; treating as no bump." | |
| fi | |
| fi | |
| echo "current_version=$current_version" >> "$GITHUB_OUTPUT" | |
| echo "previous_version=$previous_version" >> "$GITHUB_OUTPUT" | |
| echo "bumped=$bumped" >> "$GITHUB_OUTPUT" | |
| echo "Current version: $current_version" | |
| echo "Previous version: ${previous_version:-<none>}" | |
| echo "Version bumped: $bumped" | |
| docs: | |
| name: Mintlify Docs | |
| runs-on: ubuntu-latest | |
| needs: cancel-previous | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Set Up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20.17.0" | |
| - name: Install Mintlify CLI | |
| # Pin Mintlify to keep docs validation reproducible across CI runs. | |
| run: npm install --global mintlify@4.0.373 | |
| - name: Validate Docs | |
| working-directory: docs | |
| run: mintlify broken-links | |
| rustdoc: | |
| name: Rust Doc | |
| runs-on: ubuntu-latest | |
| needs: cancel-previous | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Check Documentation | |
| run: cargo rustdoc --lib -- -D missing_docs | |
| cargo-sort: | |
| name: Cargo Sort | |
| runs-on: ubuntu-latest | |
| needs: [docs, rustdoc] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Install cargo-sort | |
| run: cargo install cargo-sort | |
| - name: Check Cargo.toml Formatting | |
| run: cargo sort --check --workspace | |
| rustfmt: | |
| name: Rustfmt | |
| runs-on: ubuntu-latest | |
| needs: [docs, rustdoc] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust Nightly | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| components: rustfmt | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Run Rustfmt | |
| run: cargo +nightly fmt -- --check | |
| clippy: | |
| name: Clippy | |
| runs-on: ubuntu-latest | |
| needs: [cargo-sort, rustfmt] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Install clippy | |
| run: rustup component add clippy | |
| - name: Run Clippy | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| cargo-build: | |
| name: Cargo Build | |
| runs-on: ubuntu-latest | |
| needs: [clippy] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Cargo build | |
| run: cargo build --locked | |
| # These tests run in parallel after clippy passes | |
| cargo-test: | |
| name: Cargo Test | |
| runs-on: ubuntu-latest | |
| needs: [clippy] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Run Tests | |
| run: cargo test --verbose | |
| # Docker UAT tests run in parallel with cargo-test | |
| # Tests both user mode and kernel mode scenarios | |
| docker-uat-tests: | |
| name: Docker UAT Tests | |
| runs-on: ubuntu-latest | |
| needs: [clippy] | |
| if: false # TEMPORARILY DISABLED - Docker UAT tests are skipped for now | |
| strategy: | |
| matrix: | |
| test-mode: [user, kernel] | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Cargo Dependencies | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build Docker image (${{ matrix.test-mode }}) | |
| run: | | |
| # Build the Docker image using CI-specific Dockerfile | |
| docker build -f tests/Dockerfile.${{ matrix.test-mode }}.ci -t systemg-${{ matrix.test-mode }}:ci . | |
| - name: Run Docker UAT tests (${{ matrix.test-mode }}) | |
| id: run-tests | |
| run: | | |
| set -e | |
| # Create a directory for test output | |
| mkdir -p test-output | |
| # Run the Docker container with appropriate settings | |
| if [ "${{ matrix.test-mode }}" = "kernel" ]; then | |
| # Kernel mode needs privileged access | |
| docker run \ | |
| --rm \ | |
| --privileged \ | |
| --cap-add SYS_ADMIN \ | |
| --cap-add NET_ADMIN \ | |
| --security-opt apparmor:unconfined \ | |
| -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ | |
| -v $(pwd)/test-output:/test-output \ | |
| systemg-${{ matrix.test-mode }}:ci | tee test-output/${{ matrix.test-mode }}.log | |
| else | |
| # User mode can run without special privileges | |
| docker run \ | |
| --rm \ | |
| -v $(pwd)/test-output:/test-output \ | |
| systemg-${{ matrix.test-mode }}:ci | tee test-output/${{ matrix.test-mode }}.log | |
| fi | |
| # Extract test results for job summary | |
| if grep -q "All.*tests passed successfully" test-output/${{ matrix.test-mode }}.log; then | |
| echo "✅ ${{ matrix.test-mode }} mode: All tests passed" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ ${{ matrix.test-mode }} mode: Some tests failed" >> $GITHUB_STEP_SUMMARY | |
| exit 1 | |
| fi | |
| - name: Upload test logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-${{ matrix.test-mode }}-logs | |
| path: test-output/${{ matrix.test-mode }}.log | |
| create-github-release: | |
| name: Create GitHub Release | |
| runs-on: ubuntu-latest | |
| needs: | |
| - cargo-test | |
| - extract-version | |
| - detect-version-bump | |
| permissions: | |
| contents: write | |
| if: >- | |
| github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.detect-version-bump.outputs.bumped == 'true' | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Create or Update release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.extract-version.outputs.tag }} | |
| name: ${{ needs.extract-version.outputs.tag }} | |
| target_commitish: ${{ github.sha }} | |
| generate_release_notes: true | |
| append_body: false | |
| make_latest: true | |
| fail_on_unmatched_files: false | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| upload-release-binaries: | |
| name: Upload Release Binaries | |
| runs-on: ${{ matrix.job.os }} | |
| permissions: | |
| contents: write | |
| needs: | |
| - cargo-test | |
| - detect-version-bump | |
| - create-github-release | |
| - extract-version | |
| if: | | |
| !cancelled() && | |
| needs.cargo-test.result == 'success' && | |
| ( | |
| (needs.create-github-release.result == 'success') || | |
| (needs.create-github-release.result == 'skipped' && github.event_name == 'release' && github.event.action == 'published') || | |
| (github.event_name == 'workflow_dispatch') | |
| ) | |
| strategy: | |
| matrix: | |
| job: | |
| - os: ubuntu-latest | |
| platform: linux | |
| target: x86_64-unknown-linux-gnu | |
| cross_image: x86_64-linux-gnu | |
| cross_dockerfile: ci/Dockerfile.x86_64-unknown-linux-gnu-clang | |
| features: linux | |
| - os: ubuntu-latest | |
| platform: linux-arm | |
| target: aarch64-unknown-linux-gnu | |
| cross_image: aarch64-linux-gnu | |
| cross_dockerfile: ci/Dockerfile.aarch64-unknown-linux-gnu-clang | |
| features: linux | |
| - os: ubuntu-latest | |
| platform: debian | |
| target: x86_64-unknown-linux-gnu | |
| cross_image: debian-x86_64-linux-gnu | |
| cross_dockerfile: ci/Dockerfile.debian-x86_64-unknown-linux-gnu-clang | |
| features: linux | |
| artifact_suffix: -debian | |
| # - os: ubuntu-latest | |
| # platform: alpine | |
| # target: x86_64-unknown-linux-musl | |
| # cross_image: x86_64-linux-musl | |
| # cross_dockerfile: ci/Dockerfile.x86_64-unknown-linux-musl-clang | |
| # features: linux | |
| # artifact_suffix: -alpine | |
| # - os: ubuntu-latest | |
| # platform: alpine-arm | |
| # target: aarch64-unknown-linux-musl | |
| # cross_image: aarch64-linux-musl | |
| # cross_dockerfile: ci/Dockerfile.aarch64-unknown-linux-musl-clang | |
| # features: linux | |
| # artifact_suffix: -alpine | |
| - os: macos-latest | |
| platform: darwin | |
| target: x86_64-apple-darwin | |
| - os: macos-latest | |
| platform: darwin-arm | |
| target: aarch64-apple-darwin | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| if: matrix.job.cross_image | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Setup custom cross env ${{ matrix.job.cross_image }} | |
| if: matrix.job.cross_image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ci | |
| file: ${{ matrix.job.cross_dockerfile }} | |
| tags: ${{ matrix.job.cross_image }}:latest | |
| load: true | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Install packages (macOS) | |
| if: matrix.job.os == 'macos-latest' | |
| run: | | |
| ci/macos-install-packages.sh | |
| - name: Install toolchain | |
| uses: dtolnay/rust-toolchain@master | |
| with: | |
| toolchain: ${{ env.RUSTC_VERSION }} | |
| targets: ${{ matrix.job.target }} | |
| - name: Install cross | |
| uses: baptiste0928/cargo-install@v3 | |
| with: | |
| crate: cross | |
| cache-key: ${{ matrix.job.target }} | |
| version: "0.2.5" | |
| - name: Build sysg | |
| run: | | |
| cross \ | |
| build \ | |
| --profile=release \ | |
| --target ${{ matrix.job.target }} | |
| - name: Strip release binary linux x86_64 | |
| if: matrix.job.target == 'x86_64-unknown-linux-gnu' | |
| run: > | |
| strip "target/${{ matrix.job.target }}/release/sysg" | |
| - name: Strip release binary aarch64-linux-gnu | |
| if: matrix.job.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| docker run --rm -v \ | |
| "$PWD/target:/target:Z" \ | |
| aarch64-linux-gnu:latest \ | |
| aarch64-linux-gnu-strip \ | |
| /target/aarch64-unknown-linux-gnu/release/sysg | |
| - name: Strip release binary mac | |
| if: matrix.job.os == 'macos-latest' | |
| run: | | |
| strip -x "target/${{ matrix.job.target }}/release/sysg" | |
| - name: Prepare Binary Artifact | |
| env: | |
| PLATFORM_NAME: ${{ matrix.job.platform }} | |
| TARGET: ${{ matrix.job.target }} | |
| ARTIFACT_SUFFIX: ${{ matrix.job.artifact_suffix || '' }} | |
| run: > | |
| # Get version from either the tag (for auto-release) or GITHUB_REF (for manual releases) | |
| if [ "${{ needs.extract-version.outputs.tag }}" != "" ]; then | |
| SYSG_VERSION="${{ needs.extract-version.outputs.tag }}" | |
| else | |
| SYSG_VERSION="${GITHUB_REF#refs/tags/}" | |
| fi | |
| # optionally trim v from tag prefix | |
| SYSG_VERSION="${SYSG_VERSION#v}" | |
| echo "version is: $SYSG_VERSION" | |
| # setup artifact filename | |
| SYSG_ARTIFACT="sysg-$SYSG_VERSION-$TARGET$ARTIFACT_SUFFIX" | |
| SYSG_ZIP_FILE_NAME="$SYSG_ARTIFACT.tar.gz" | |
| echo "SYSG_ZIP_FILE_NAME=$SYSG_ZIP_FILE_NAME" >> $GITHUB_ENV | |
| # create zip file | |
| mkdir -pv "$SYSG_ARTIFACT" | |
| cp "target/${{ matrix.job.target }}/release/sysg" "$SYSG_ARTIFACT" | |
| tar -czvf "$SYSG_ZIP_FILE_NAME" "$SYSG_ARTIFACT" | |
| - name: Upload Sysg Artifact | |
| uses: softprops/action-gh-release@v1 | |
| if: github.event_name == 'release' || needs.create-github-release.result == 'success' | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| with: | |
| tag_name: ${{ needs.extract-version.outputs.tag }} | |
| files: ./${{ env.SYSG_ZIP_FILE_NAME }} | |
| # - name: Notify if Job Fails | |
| # uses: ravsamhq/notify-slack-action@v2 | |
| # if: always() && (github.ref == 'refs/heads/master' || github.ref_type == 'tag') | |
| # && matrix.job.os != 'macos-latest' | |
| # with: | |
| # status: ${{ job.status }} | |
| # token: ${{ secrets.GITHUB_TOKEN }} | |
| # notification_title: "{workflow} has {status_message}" | |
| # message_format: "{emoji} *{workflow}* {status_message} in <{repo_url}|{repo}> : | |
| # <{run_url}|View Run Results>" | |
| # footer: "" | |
| # notify_when: failure | |
| # env: | |
| # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_NOTIFY_BUILD }} | |
| upload-s3: | |
| name: Upload Binaries to S3 | |
| needs: | |
| - upload-release-binaries | |
| - extract-version | |
| if: | | |
| !cancelled() && | |
| needs.upload-release-binaries.result == 'success' | |
| environment: ci | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download and upload release assets to AWS S3 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| RELEASE_TAG: ${{ needs.extract-version.outputs.tag }} | |
| AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| AWS_DEFAULT_REGION: us-east-2 # change if needed | |
| S3_BUCKET: sh.sysg.dev | |
| run: | | |
| set -euo pipefail | |
| # Get release ID from tag (works for both auto-release and manual releases) | |
| if [ -n "${{ github.event.release.id }}" ]; then | |
| RELEASE_ID="${{ github.event.release.id }}" | |
| else | |
| # Fetch release ID by tag for auto-releases | |
| RELEASE_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" \ | |
| | jq -r '.id') | |
| fi | |
| echo "Fetching .tar.gz assets for release ID $RELEASE_ID ..." | |
| while read -r url name; do | |
| echo "Downloading $name from $url ..." | |
| curl -sSL -H "Authorization: token $GITHUB_TOKEN" -o "$name" "$url" | |
| echo "Uploading $name to s3://${S3_BUCKET}/${name} ..." | |
| aws s3 cp "$name" "s3://${S3_BUCKET}/${name}" --content-type "application/gzip" | |
| echo "Uploaded $name successfully" | |
| done < <( | |
| curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets" \ | |
| | jq -r '.[] | select(.name | endswith(".tar.gz")) | "\(.browser_download_url) \(.name)"' | |
| ) | |
| cargo-publish: | |
| name: Publish to crates.io | |
| runs-on: ubuntu-latest | |
| needs: | |
| - cargo-test | |
| - detect-version-bump | |
| - create-github-release | |
| - upload-s3 | |
| if: | | |
| !cancelled() && | |
| needs.cargo-test.result == 'success' && | |
| needs.upload-s3.result == 'success' && | |
| ( | |
| ( | |
| github.event_name == 'push' && | |
| github.ref == 'refs/heads/main' && | |
| needs.create-github-release.result == 'success' | |
| ) || | |
| (github.event_name == 'workflow_dispatch') | |
| ) | |
| environment: ci | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Publish Crate | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CRATESIO_TOKEN }} | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ github.event.inputs.test-release }}" == "true" ]; then | |
| cargo publish --dry-run --locked --no-verify | |
| else | |
| cargo publish --locked --no-verify | |
| fi |