# Copyright 2024 RustFS Team # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Build and Release on: push: tags: ["v*"] branches: [main] paths: - "rustfs/**" - "cli/**" - "crates/**" - "Cargo.toml" - "Cargo.lock" - ".github/workflows/build.yml" pull_request: branches: [main] paths: - "rustfs/**" - "cli/**" - "crates/**" - "Cargo.toml" - "Cargo.lock" - ".github/workflows/build.yml" schedule: - cron: "0 0 * * 0" # Weekly on Sunday at midnight UTC workflow_dispatch: inputs: force_build: description: "Force build even without changes" required: false default: false type: boolean env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 # Optimize build performance CARGO_INCREMENTAL: 0 RUSTFLAGS: "-C target-cpu=native" jobs: # First layer: GitHub Actions level optimization (handling duplicates and concurrency) skip-duplicate: name: Skip Duplicate Actions runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - name: Skip duplicate actions id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: concurrent_skipping: "same_content_newer" cancel_others: true paths_ignore: '["*.md", "docs/**", "deploy/**", "scripts/dev_*.sh"]' # Second layer: Business logic level checks (handling build strategy) build-check: name: Build Strategy Check needs: skip-duplicate if: needs.skip-duplicate.outputs.should_skip != 'true' runs-on: ubuntu-latest outputs: should_build: ${{ steps.check.outputs.should_build }} build_type: ${{ steps.check.outputs.build_type }} steps: - name: Determine build strategy id: check run: | should_build=false build_type="none" # Business logic: when we need to build if [[ "${{ github.event_name }}" == "schedule" ]] || \ [[ "${{ github.event_name }}" == "workflow_dispatch" ]] || \ [[ "${{ github.event.inputs.force_build }}" == "true" ]] || \ [[ "${{ contains(github.event.head_commit.message, '--build') }}" == "true" ]]; then should_build=true build_type="development" fi if [[ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]]; then should_build=true build_type="release" fi echo "should_build=$should_build" >> $GITHUB_OUTPUT echo "build_type=$build_type" >> $GITHUB_OUTPUT echo "Build needed: $should_build (type: $build_type)" # Build RustFS binaries build-rustfs: name: Build RustFS needs: [skip-duplicate, build-check] if: needs.skip-duplicate.outputs.should_skip != 'true' && needs.build-check.outputs.should_build == 'true' runs-on: ${{ matrix.os }} timeout-minutes: 60 strategy: fail-fast: false matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-musl cross: false - os: ubuntu-latest target: aarch64-unknown-linux-musl cross: true - os: macos-latest target: aarch64-apple-darwin cross: false steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Rust environment uses: ./.github/actions/setup with: rust-version: stable target: ${{ matrix.target }} cache-shared-key: build-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} github-token: ${{ secrets.GITHUB_TOKEN }} cache-save-if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} install-cross-tools: ${{ matrix.cross }} - name: Download static console assets run: | mkdir -p ./rustfs/static curl -L "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" \ -o console.zip --retry 3 --retry-delay 5 --max-time 300 unzip -o console.zip -d ./rustfs/static rm console.zip - name: Build RustFS run: | touch rustfs/build.rs if [[ "${{ matrix.cross }}" == "true" ]]; then cargo zigbuild --release --target ${{ matrix.target }} -p rustfs --bins else cargo build --release --target ${{ matrix.target }} -p rustfs --bins fi - name: Create release package id: package run: | PACKAGE_NAME="rustfs-${{ matrix.target }}" mkdir -p "${PACKAGE_NAME}"/{bin,docs} # Copy binary if [[ "${{ matrix.target }}" == *"windows"* ]]; then cp target/${{ matrix.target }}/release/rustfs.exe "${PACKAGE_NAME}/bin/" else cp target/${{ matrix.target }}/release/rustfs "${PACKAGE_NAME}/bin/" chmod +x "${PACKAGE_NAME}/bin/rustfs" fi # Copy documentation [ -f "LICENSE" ] && cp LICENSE "${PACKAGE_NAME}/docs/" [ -f "README.md" ] && cp README.md "${PACKAGE_NAME}/docs/" # Create archive tar -czf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}" echo "package_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT echo "Package created: ${PACKAGE_NAME}.tar.gz" - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: ${{ steps.package.outputs.package_name }} path: ${{ steps.package.outputs.package_name }}.tar.gz retention-days: ${{ startsWith(github.ref, 'refs/tags/') && 30 || 7 }} # Build GUI (only for releases) build-gui: name: Build GUI needs: [skip-duplicate, build-check, build-rustfs] if: needs.skip-duplicate.outputs.should_skip != 'true' && needs.build-check.outputs.build_type == 'release' runs-on: ${{ matrix.os }} timeout-minutes: 45 strategy: fail-fast: false matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-musl platform: linux - os: macos-latest target: aarch64-apple-darwin platform: macos steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Rust environment uses: ./.github/actions/setup with: rust-version: stable target: ${{ matrix.target }} cache-shared-key: gui-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} github-token: ${{ secrets.GITHUB_TOKEN }} - name: Download RustFS binary uses: actions/download-artifact@v4 with: name: rustfs-${{ matrix.target }} path: ./artifacts - name: Prepare embedded binary run: | mkdir -p ./cli/rustfs-gui/embedded-rustfs/ tar -xzf ./artifacts/rustfs-${{ matrix.target }}.tar.gz -C ./artifacts/ cp ./artifacts/rustfs-${{ matrix.target }}/bin/rustfs ./cli/rustfs-gui/embedded-rustfs/ - name: Install Dioxus CLI uses: taiki-e/cache-cargo-install-action@v2 with: tool: dioxus-cli - name: Build GUI working-directory: ./cli/rustfs-gui run: | case "${{ matrix.platform }}" in "linux") dx bundle --platform linux --package-types deb --package-types appimage --release ;; "macos") dx bundle --platform macos --package-types dmg --release ;; esac - name: Package GUI id: gui_package run: | GUI_PACKAGE="rustfs-gui-${{ matrix.target }}" mkdir -p "${GUI_PACKAGE}" # Copy GUI bundles if [[ -d "cli/rustfs-gui/dist/bundle" ]]; then cp -r cli/rustfs-gui/dist/bundle/* "${GUI_PACKAGE}/" fi tar -czf "${GUI_PACKAGE}.tar.gz" "${GUI_PACKAGE}" echo "gui_package=${GUI_PACKAGE}" >> $GITHUB_OUTPUT - name: Upload GUI artifacts uses: actions/upload-artifact@v4 with: name: ${{ steps.gui_package.outputs.gui_package }} path: ${{ steps.gui_package.outputs.gui_package }}.tar.gz retention-days: 30 # Release management release: name: GitHub Release needs: [skip-duplicate, build-check, build-rustfs, build-gui] if: always() && needs.skip-duplicate.outputs.should_skip != 'true' && needs.build-check.outputs.build_type == 'release' runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: ./release-artifacts - name: Prepare release id: release_prep run: | VERSION="${GITHUB_REF#refs/tags/}" VERSION_CLEAN="${VERSION#v}" echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "version_clean=${VERSION_CLEAN}" >> $GITHUB_OUTPUT # Organize artifacts mkdir -p ./release-files find ./release-artifacts -name "*.tar.gz" -exec cp {} ./release-files/ \; # Create release notes cat > release_notes.md << EOF ## RustFS ${VERSION_CLEAN} ### 🚀 Downloads **Linux:** - \`rustfs-x86_64-unknown-linux-musl.tar.gz\` - Linux x86_64 (static) - \`rustfs-aarch64-unknown-linux-musl.tar.gz\` - Linux ARM64 (static) **macOS:** - \`rustfs-aarch64-apple-darwin.tar.gz\` - macOS Apple Silicon **GUI Applications:** - \`rustfs-gui-*.tar.gz\` - GUI applications ### 📦 Installation 1. Download the appropriate binary for your platform 2. Extract: \`tar -xzf rustfs-*.tar.gz\` 3. Install: \`sudo cp rustfs-*/bin/rustfs /usr/local/bin/\` ### 🔗 Mirror Downloads - [OSS Mirror](https://rustfs-artifacts.oss-cn-beijing.aliyuncs.com/artifacts/rustfs/) EOF - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.release_prep.outputs.version }} name: "RustFS ${{ steps.release_prep.outputs.version_clean }}" body_path: release_notes.md files: ./release-files/*.tar.gz draft: false prerelease: ${{ contains(steps.release_prep.outputs.version, 'alpha') || contains(steps.release_prep.outputs.version, 'beta') || contains(steps.release_prep.outputs.version, 'rc') }} # Upload to OSS (optional) upload-oss: name: Upload to OSS needs: [skip-duplicate, build-check, build-rustfs] if: always() && needs.skip-duplicate.outputs.should_skip != 'true' && needs.build-check.outputs.build_type == 'release' && needs.build-rustfs.result == 'success' runs-on: ubuntu-latest env: OSS_ACCESS_KEY_ID: ${{ secrets.ALICLOUDOSS_KEY_ID }} OSS_ACCESS_KEY_SECRET: ${{ secrets.ALICLOUDOSS_KEY_SECRET }} steps: - name: Download artifacts uses: actions/download-artifact@v4 with: path: ./artifacts - name: Upload to Aliyun OSS if: ${{ env.OSS_ACCESS_KEY_ID != '' }} run: | # Install ossutil curl -o ossutil.zip https://gosspublic.alicdn.com/ossutil/v2/2.1.1/ossutil-2.1.1-linux-amd64.zip unzip ossutil.zip sudo mv ossutil-*/ossutil /usr/local/bin/ # Upload files find ./artifacts -name "*.tar.gz" -exec ossutil cp {} oss://rustfs-artifacts/artifacts/rustfs/ --force \; # Create latest.json VERSION="${GITHUB_REF#refs/tags/v}" echo "{\"version\":\"${VERSION}\",\"release_date\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > latest.json ossutil cp latest.json oss://rustfs-version/latest.json --force