Skip to content

Build and Release

Build and Release #8

Workflow file for this run

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 }} --target-dir 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 内搜索打包输出,不依赖固定路径
mapfile -t APP_DIRS < <(find src-tauri target -path "*bundle/macos/*.app" -type d 2>/dev/null || true)
mapfile -t DMG_FILES < <(find src-tauri target -path "*bundle/dmg/*.dmg" -type f 2>/dev/null || true)
echo "发现的 .app 目录:" && printf ' - %s\n' "${APP_DIRS[@]:-无}"
echo "发现的 .dmg 文件:" && printf ' - %s\n' "${DMG_FILES[@]:-无}"
# Copy and rename DMG
if [ ${#DMG_FILES[@]} -gt 0 ]; then
cp "${DMG_FILES[0]}" "artifacts/${ARTIFACT_PREFIX}.dmg"
echo "✅ Created: ${ARTIFACT_PREFIX}.dmg"
fi
# Create and rename app.zip
if [ ${#APP_DIRS[@]} -gt 0 ]; then
FIRST_APP="${APP_DIRS[0]}"
APP_PARENT="$(dirname "$FIRST_APP")"
APP_NAME="$(basename "$FIRST_APP")"
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 }}