Skip to content

v0.0.3

v0.0.3 #16

Workflow file for this run

name: Build and Release
on:
push:
tags:
- "*.*.*"
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Release tag (e.g., 0.1.0). If provided, will build in release mode."
required: false
type: string
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"
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ -n "${{ github.event.inputs.tag }}" ]]; then
# Manual trigger with tag input - treat as release
version="${{ github.event.inputs.tag }}"
is_release=true
echo "🏷️ Manual release build detected: $version"
else
# Manual trigger without tag or other
version="dev-$(git rev-parse --short HEAD)"
echo "🛠️ Development build detected: $version"
fi
# Ensure version starts with 'v' for release builds
if [[ "$is_release" == "true" ]] && [[ ! "$version" =~ ^v ]]; then
version="v${version}"
echo "ℹ️ Version normalized to: $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"
# 调试:列出可能的构建输出目录
$targetPath = "target/${{ matrix.rust_target }}/release/bundle"
if (-not (Test-Path $targetPath)) {
$targetPath = "src-tauri/target/${{ matrix.rust_target }}/release/bundle"
}
Write-Host "🔍 搜索构建产物目录: $targetPath"
if (Test-Path $targetPath) {
Write-Host "📁 Bundle 目录存在,内容:"
Get-ChildItem -Path $targetPath -Recurse -File | Select-Object FullName, Length | Format-Table -AutoSize
} else {
Write-Host "❌ Bundle 目录不存在: $targetPath"
}
$artifactCount = 0
# Copy and rename MSI
# $msiPath = "$targetPath/msi"
# Write-Host "🔍 搜索 MSI 文件: $msiPath"
# $msiFiles = Get-ChildItem -Path $msiPath -Filter "*.msi" -ErrorAction SilentlyContinue
# if ($msiFiles) {
# $destPath = "artifacts/$ARTIFACT_PREFIX.msi"
# Copy-Item $msiFiles[0].FullName -Destination $destPath
# Write-Host "✅ Created: $destPath"
# $artifactCount++
# } else {
# Write-Host "⚠️ 未找到 MSI 文件在: $msiPath"
# }
# Copy and rename EXE (NSIS installer)
$nsisPath = "$targetPath/nsis"
Write-Host "🔍 搜索 NSIS EXE 文件: $nsisPath"
$exeFiles = Get-ChildItem -Path $nsisPath -Filter "*.exe" -ErrorAction SilentlyContinue
if ($exeFiles) {
$destPath = "artifacts/$ARTIFACT_PREFIX-setup.exe"
Copy-Item $exeFiles[0].FullName -Destination $destPath
Write-Host "✅ Created: $destPath"
$artifactCount++
} else {
Write-Host "⚠️ 未找到 NSIS EXE 文件在: $nsisPath"
}
# 检查是否至少生成了一个产物
if ($artifactCount -eq 0) {
Write-Host "❌ 错误:未找到任何构建产物(MSI 或 EXE)"
Write-Host "请检查构建是否成功完成"
exit 1
}
# 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"
}
# 只保留 NSIS EXE 文件,删除 MSI 文件
Get-ChildItem -Path "artifacts" -Filter "*.msi" | Remove-Item -Force
Write-Host "🗑️ 已删除 MSI 文件,只保留 EXE 安装包"
Write-Host "📦 最终 artifacts 列表:"
Get-ChildItem -Path artifacts | Format-Table Name, Length -AutoSize
- 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 }}