Build and Release #10
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 and Release | |
| on: | |
| push: | |
| tags: | |
| - '*.*.*' | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| jobs: | |
| # Build strategy check - determine build type and version | |
| build-check: | |
| name: Build Strategy Check | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| should_build: ${{ steps.check.outputs.should_build }} | |
| version: ${{ steps.check.outputs.version }} | |
| is_release: ${{ steps.check.outputs.is_release }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine build strategy | |
| id: check | |
| run: | | |
| should_build=true | |
| version="" | |
| is_release=false | |
| if [[ "${{ github.event_name }}" == "release" ]]; then | |
| # Release event - get version from release tag | |
| version="${{ github.event.release.tag_name }}" | |
| is_release=true | |
| echo "📦 Release build detected: $version" | |
| elif [[ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]]; then | |
| # Tag push - get version from tag | |
| version="${GITHUB_REF#refs/tags/}" | |
| is_release=true | |
| echo "🏷️ Tag build detected: $version" | |
| else | |
| # Manual trigger or other | |
| version="dev-$(git rev-parse --short HEAD)" | |
| echo "🛠️ Development build detected: $version" | |
| fi | |
| echo "should_build=$should_build" >> $GITHUB_OUTPUT | |
| echo "version=$version" >> $GITHUB_OUTPUT | |
| echo "is_release=$is_release" >> $GITHUB_OUTPUT | |
| echo "📊 Build Summary:" | |
| echo " - Should build: $should_build" | |
| echo " - Version: $version" | |
| echo " - Is release: $is_release" | |
| build: | |
| name: Build | |
| needs: build-check | |
| if: needs.build-check.outputs.should_build == 'true' | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: 'macos-latest' | |
| rust_target: 'aarch64-apple-darwin' | |
| os_name: 'macos' | |
| arch: 'aarch64' | |
| - platform: 'macos-13' | |
| rust_target: 'x86_64-apple-darwin' | |
| os_name: 'macos' | |
| arch: 'x86_64' | |
| - platform: 'windows-latest' | |
| rust_target: 'x86_64-pc-windows-msvc' | |
| os_name: 'windows' | |
| arch: 'x86_64' | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.rust_target }} | |
| - name: Install WebAssembly target | |
| run: rustup target add wasm32-unknown-unknown | |
| - name: Setup Rust cache | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: src-tauri | |
| - name: Install dependencies (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| brew install trunk | |
| cargo install tauri-cli | |
| - name: Install dependencies (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| cargo install trunk tauri-cli | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Download RustFS binary (macOS aarch64) | |
| if: matrix.platform == 'macos-latest' | |
| run: | | |
| mkdir -p src-tauri/binaries | |
| curl -L -o rustfs-macos-aarch64.zip https://dl.rustfs.com/artifacts/rustfs/release/rustfs-macos-aarch64-latest.zip | |
| unzip -q rustfs-macos-aarch64.zip -d rustfs-temp | |
| cp rustfs-temp/rustfs src-tauri/binaries/rustfs-macos-aarch64 | |
| chmod +x src-tauri/binaries/rustfs-macos-aarch64 | |
| rm -rf rustfs-temp rustfs-macos-aarch64.zip | |
| - name: Download RustFS binary (macOS x86_64) | |
| if: matrix.platform == 'macos-13' | |
| run: | | |
| mkdir -p src-tauri/binaries | |
| curl -L -o rustfs-macos-x86_64.zip https://dl.rustfs.com/artifacts/rustfs/release/rustfs-macos-x86_64-latest.zip | |
| unzip -q rustfs-macos-x86_64.zip -d rustfs-temp | |
| cp rustfs-temp/rustfs src-tauri/binaries/rustfs-macos-x86_64 | |
| chmod +x src-tauri/binaries/rustfs-macos-x86_64 | |
| rm -rf rustfs-temp rustfs-macos-x86_64.zip | |
| - name: Download RustFS binary (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force -Path src-tauri\binaries | |
| Invoke-WebRequest -Uri "https://dl.rustfs.com/artifacts/rustfs/release/rustfs-windows-x86_64-latest.zip" -OutFile "rustfs-windows-x86_64.zip" | |
| Expand-Archive -Path "rustfs-windows-x86_64.zip" -DestinationPath "rustfs-temp" -Force | |
| Copy-Item "rustfs-temp\rustfs.exe" -Destination "src-tauri\binaries\rustfs-windows-x86_64.exe" | |
| Remove-Item -Recurse -Force rustfs-temp, rustfs-windows-x86_64.zip | |
| - name: Build Tauri application | |
| run: cargo tauri build --target ${{ matrix.rust_target }} | |
| - name: Prepare and rename artifacts (macOS) | |
| if: runner.os == 'macOS' | |
| env: | |
| VERSION: ${{ needs.build-check.outputs.version }} | |
| run: | | |
| mkdir -p artifacts | |
| # 允许空 glob 返回空数组,避免 zip 在找不到文件时直接失败 | |
| shopt -s nullglob | |
| # Format: rustfs-launcher-{os}-{arch}-{version} | |
| ARTIFACT_PREFIX="rustfs-launcher-${{ matrix.os_name }}-${{ matrix.arch }}-${VERSION}" | |
| # 在 workspace 内搜索打包输出,不依赖固定路径 | |
| APP_DIR=$(find src-tauri target -path "*bundle/macos/*.app" -type d -print -quit 2>/dev/null || true) | |
| DMG_FILE=$(find src-tauri target -path "*bundle/dmg/*.dmg" -type f -print -quit 2>/dev/null || true) | |
| echo "发现的 .app 目录:${APP_DIR:-无}" | |
| echo "发现的 .dmg 文件:${DMG_FILE:-无}" | |
| # Copy and rename DMG | |
| if [ -n "$DMG_FILE" ]; then | |
| cp "$DMG_FILE" "artifacts/${ARTIFACT_PREFIX}.dmg" | |
| echo "✅ Created: ${ARTIFACT_PREFIX}.dmg" | |
| fi | |
| # Create and rename app.zip | |
| if [ -n "$APP_DIR" ]; then | |
| APP_PARENT="$(dirname "$APP_DIR")" | |
| APP_NAME="$(basename "$APP_DIR")" | |
| cd "$APP_PARENT" | |
| zip -r "$GITHUB_WORKSPACE/artifacts/${ARTIFACT_PREFIX}.app.zip" "$APP_NAME" | |
| echo "✅ Created: ${ARTIFACT_PREFIX}.app.zip" | |
| else | |
| echo "❌ 未找到 .app 产物" | |
| exit 1 | |
| fi | |
| # Create latest version files | |
| cd "$GITHUB_WORKSPACE/artifacts" | |
| LATEST_PREFIX="rustfs-launcher-${{ matrix.os_name }}-${{ matrix.arch }}-latest" | |
| for file in rustfs-launcher-*-${VERSION}.*; do | |
| if [ -f "$file" ]; then | |
| ext="${file##*.}" | |
| cp "$file" "${LATEST_PREFIX}.${ext}" | |
| echo "✅ Created latest: ${LATEST_PREFIX}.${ext}" | |
| fi | |
| done | |
| # 确保确实生成了产物 | |
| if [ $(ls -1 | wc -l) -eq 0 ]; then | |
| echo "❌ 未生成任何 artifacts" | |
| exit 1 | |
| fi | |
| ls -la "$GITHUB_WORKSPACE/artifacts" | |
| - name: Prepare and rename artifacts (Windows) | |
| if: runner.os == 'Windows' | |
| env: | |
| VERSION: ${{ needs.build-check.outputs.version }} | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force -Path artifacts | |
| # Format: rustfs-launcher-{os}-{arch}-{version} | |
| $ARTIFACT_PREFIX = "rustfs-launcher-${{ matrix.os_name }}-${{ matrix.arch }}-$env:VERSION" | |
| # Copy and rename MSI | |
| $msiFiles = Get-ChildItem -Path "src-tauri/target/${{ matrix.rust_target }}/release/bundle/msi" -Filter "*.msi" -ErrorAction SilentlyContinue | |
| if ($msiFiles) { | |
| Copy-Item $msiFiles[0].FullName -Destination "artifacts/${ARTIFACT_PREFIX}.msi" | |
| Write-Host "✅ Created: ${ARTIFACT_PREFIX}.msi" | |
| } | |
| # Copy and rename EXE (NSIS installer) | |
| $exeFiles = Get-ChildItem -Path "src-tauri/target/${{ matrix.rust_target }}/release/bundle/nsis" -Filter "*.exe" -ErrorAction SilentlyContinue | |
| if ($exeFiles) { | |
| Copy-Item $exeFiles[0].FullName -Destination "artifacts/${ARTIFACT_PREFIX}-setup.exe" | |
| Write-Host "✅ Created: ${ARTIFACT_PREFIX}-setup.exe" | |
| } | |
| # Create latest version files | |
| $LATEST_PREFIX = "rustfs-launcher-${{ matrix.os_name }}-${{ matrix.arch }}-latest" | |
| Get-ChildItem -Path "artifacts" -Filter "rustfs-launcher-*-$env:VERSION.*" | ForEach-Object { | |
| $ext = $_.Extension | |
| $latestName = "${LATEST_PREFIX}${ext}" | |
| if ($_.Name -like "*-setup.exe") { | |
| $latestName = "${LATEST_PREFIX}-setup.exe" | |
| } | |
| Copy-Item $_.FullName -Destination "artifacts/$latestName" | |
| Write-Host "✅ Created latest: $latestName" | |
| } | |
| Get-ChildItem -Path artifacts | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: rustfs-launcher-${{ matrix.os_name }}-${{ matrix.arch }} | |
| path: artifacts/* | |
| if-no-files-found: error | |
| - name: Upload to Aliyun OSS | |
| if: needs.build-check.outputs.is_release == 'true' | |
| env: | |
| OSS_ACCESS_KEY_ID: ${{ secrets.ALICLOUDOSS_KEY_ID }} | |
| OSS_ACCESS_KEY_SECRET: ${{ secrets.ALICLOUDOSS_KEY_SECRET }} | |
| OSS_REGION: cn-beijing | |
| OSS_ENDPOINT: https://oss-cn-beijing.aliyuncs.com | |
| shell: bash | |
| run: | | |
| if [[ -z "$OSS_ACCESS_KEY_ID" ]]; then | |
| echo "⚠️ OSS credentials not available, skipping OSS upload" | |
| exit 0 | |
| fi | |
| # Install ossutil | |
| OSSUTIL_VERSION="2.1.1" | |
| case "${{ matrix.os_name }}" in | |
| macos) | |
| if [[ "$(uname -m)" == "arm64" ]]; then | |
| ARCH="arm64" | |
| else | |
| ARCH="amd64" | |
| fi | |
| OSSUTIL_ZIP="ossutil-${OSSUTIL_VERSION}-mac-${ARCH}.zip" | |
| OSSUTIL_DIR="ossutil-${OSSUTIL_VERSION}-mac-${ARCH}" | |
| curl -o "$OSSUTIL_ZIP" "https://gosspublic.alicdn.com/ossutil/v2/${OSSUTIL_VERSION}/${OSSUTIL_ZIP}" | |
| unzip "$OSSUTIL_ZIP" | |
| mv "${OSSUTIL_DIR}/ossutil" /usr/local/bin/ | |
| rm -rf "$OSSUTIL_DIR" "$OSSUTIL_ZIP" | |
| chmod +x /usr/local/bin/ossutil | |
| OSSUTIL_BIN=ossutil | |
| ;; | |
| windows) | |
| OSSUTIL_ZIP="ossutil-${OSSUTIL_VERSION}-windows-amd64.zip" | |
| OSSUTIL_DIR="ossutil-${OSSUTIL_VERSION}-windows-amd64" | |
| curl -o "$OSSUTIL_ZIP" "https://gosspublic.alicdn.com/ossutil/v2/${OSSUTIL_VERSION}/${OSSUTIL_ZIP}" | |
| unzip "$OSSUTIL_ZIP" | |
| mv "${OSSUTIL_DIR}/ossutil.exe" ./ossutil.exe | |
| rm -rf "$OSSUTIL_DIR" "$OSSUTIL_ZIP" | |
| OSSUTIL_BIN=./ossutil.exe | |
| ;; | |
| esac | |
| OSS_PATH="oss://rustfs-artifacts/artifacts/rustfs-launcher/release/" | |
| # Upload all artifacts to OSS | |
| echo "📤 Uploading artifacts to $OSS_PATH..." | |
| for file in artifacts/*; do | |
| if [[ -f "$file" ]]; then | |
| echo "Uploading: $file to $OSS_PATH..." | |
| $OSSUTIL_BIN cp "$file" "$OSS_PATH" --force | |
| echo "✅ Uploaded: $file" | |
| fi | |
| done | |
| echo "✅ OSS upload completed successfully" | |
| # Upload release assets | |
| upload-release-assets: | |
| name: Upload Release Assets | |
| needs: [build-check, build] | |
| runs-on: ubuntu-latest | |
| if: needs.build-check.outputs.is_release == 'true' | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: rustfs-launcher-* | |
| merge-multiple: true | |
| - name: Display structure of downloaded files | |
| run: ls -R artifacts | |
| - name: Prepare release assets | |
| run: | | |
| mkdir -p release-assets | |
| # Copy all artifacts | |
| if [ -d "artifacts" ] && [ "$(ls -A artifacts)" ]; then | |
| cp artifacts/* release-assets/ | |
| echo "✅ Artifacts copied to release-assets" | |
| else | |
| echo "⚠️ No artifacts found to copy" | |
| fi | |
| # Generate checksums | |
| cd release-assets | |
| if ls *.dmg *.zip *.msi *.exe 2>/dev/null; then | |
| sha256sum * > SHA256SUMS | |
| echo "✅ SHA256SUMS generated" | |
| else | |
| echo "⚠️ No files found for checksum generation" | |
| fi | |
| echo "📦 Release assets:" | |
| ls -la | |
| - name: Upload to GitHub Release (release event) | |
| if: github.event_name == 'release' | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ github.event.release.tag_name }} | |
| files: release-assets/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload to GitHub Release (tag push) | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ needs.build-check.outputs.version }} | |
| files: release-assets/* | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |