Skip to content

Release

Release #6

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag_name:
description: "Release tag (手动运行可自定义,留空则使用 manual-<short_sha>)"
required: false
type: string
release_name:
description: "Release 名称(可选,默认与 tag 相同)"
required: false
type: string
prerelease:
description: "是否标记为 Pre-release(手动运行默认 true)"
required: false
default: true
type: boolean
create_release:
description: "是否创建 GitHub Release(手动运行可设为 false,仅构建与产出 Actions Artifacts)"
required: false
default: false
type: boolean
permissions:
contents: write
env:
FLUTTER_VERSION: "3.27.3"
jobs:
android:
name: Android (APK + AAB)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "17"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install Android build-tools and platforms
run: |
sdkmanager --install "platform-tools" "platforms;android-34" "build-tools;34.0.0"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: "stable"
cache: true
- name: Flutter pub get
run: flutter pub get
- name: Build APK (release)
run: flutter build apk --release
- name: Build App Bundle (AAB)
run: flutter build appbundle --release
- name: List Android outputs (debug)
run: |
echo "== build/app/outputs =="
ls -R build/app/outputs || true
- name: Upload Android artifacts
uses: actions/upload-artifact@v4
with:
name: android
if-no-files-found: error
path: |
build/app/outputs/flutter-apk/*.apk
build/app/outputs/bundle/release/*.aab
windows:
name: Windows (zip)
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: "stable"
cache: true
- name: Enable Windows desktop
run: flutter config --enable-windows-desktop
- name: Flutter pub get
run: flutter pub get
- name: Build Windows (release)
run: flutter build windows --release
- name: Package Windows zip
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$src = "build/windows/x64/runner/Release"
if (!(Test-Path $src)) { Get-ChildItem -Recurse build/windows | Write-Output }
$dst = "xplayer-windows-x64.zip"
if (Test-Path $dst) { Remove-Item $dst }
Compress-Archive -Path "$src/*" -DestinationPath $dst
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: windows
if-no-files-found: error
path: xplayer-windows-x64.zip
macos:
name: macOS (app.zip)
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: "stable"
cache: true
- name: Enable macOS desktop
run: flutter config --enable-macos-desktop
- name: Flutter pub get
run: flutter pub get
- name: Build macOS (release)
run: flutter build macos --release
- name: Package macOS .app to zip (unsigned)
run: |
set -euo pipefail
APP_PATH=$(ls -d build/macos/Build/Products/Release/*.app | head -n 1)
echo "Found app: $APP_PATH"
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" xplayer-macos.zip
- name: Package macOS .app to DMG (unsigned)
run: |
set -euo pipefail
APP_PATH=$(ls -d build/macos/Build/Products/Release/*.app | head -n 1)
VOLUME_NAME="xplayer"
DMG_PATH="xplayer-macos.dmg"
[ -f "$DMG_PATH" ] && rm -f "$DMG_PATH"
# 创建临时目录以生成 DMG 内容
TMPDIR=$(mktemp -d)
cp -R "$APP_PATH" "$TMPDIR/"
hdiutil create -volname "$VOLUME_NAME" -srcfolder "$TMPDIR" -ov -format UDZO "$DMG_PATH"
rm -rf "$TMPDIR"
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: macos
if-no-files-found: error
path: |
xplayer-macos.zip
xplayer-macos.dmg
ios:
name: iOS (app.zip + unsigned .ipa)
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: "stable"
cache: true
- name: Flutter pub get
run: flutter pub get
- name: Build iOS (no codesign)
run: flutter build ios --release --no-codesign
- name: Package iOS Runner.app (unsigned)
run: |
set -euo pipefail
APP_PATH="build/ios/iphoneos/Runner.app"
if [ ! -d "$APP_PATH" ]; then
echo "Runner.app not found at $APP_PATH" >&2
ls -la build/ios/iphoneos || true
exit 1
fi
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" Runner-iphoneos.app.zip
mkdir -p Payload
cp -R "$APP_PATH" Payload/
/usr/bin/zip -qry Runner-unsigned.ipa Payload
- name: Upload iOS artifacts
uses: actions/upload-artifact@v4
with:
name: ios
path: |
Runner-iphoneos.app.zip
Runner-unsigned.ipa
release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [android, windows, macos, ios]
steps:
- name: Prepare release metadata
id: meta
shell: bash
run: |
set -euo pipefail
IS_TAG="false"
TAG_NAME=""
if [[ "${GITHUB_REF:-}" == refs/tags/* ]]; then
IS_TAG="true"
TAG_NAME="${GITHUB_REF#refs/tags/}"
fi
if [[ -z "$TAG_NAME" ]]; then
INPUT_TAG="${{ github.event.inputs.tag_name || '' }}"
if [[ -n "$INPUT_TAG" ]]; then
TAG_NAME="$INPUT_TAG"
else
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
TAG_NAME="manual-${SHORT_SHA}"
fi
fi
# 手动触发默认 prerelease=true;tag 触发默认 false
INPUT_PRERELEASE="${{ github.event.inputs.prerelease || '' }}"
if [[ "$IS_TAG" == "true" ]]; then
PRERELEASE="false"
else
PRERELEASE=${INPUT_PRERELEASE:-true}
fi
# 是否创建 Release
INPUT_CREATE="${{ github.event.inputs.create_release || '' }}"
if [[ "$IS_TAG" == "true" ]]; then
CREATE_RELEASE="true"
else
CREATE_RELEASE=${INPUT_CREATE:-false}
fi
RELEASE_NAME_INPUT="${{ github.event.inputs.release_name || '' }}"
if [[ -n "$RELEASE_NAME_INPUT" ]]; then
RELEASE_NAME="$RELEASE_NAME_INPUT"
else
RELEASE_NAME="$TAG_NAME"
fi
echo "is_tag=$IS_TAG" >> $GITHUB_OUTPUT
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
echo "prerelease=$PRERELEASE" >> $GITHUB_OUTPUT
echo "create_release=$CREATE_RELEASE" >> $GITHUB_OUTPUT
echo "release_name=$RELEASE_NAME" >> $GITHUB_OUTPUT
- name: Download Android artifacts
uses: actions/download-artifact@v4
with:
name: android
path: ./dist/android
- name: List downloaded artifacts (debug)
run: |
echo "== dist tree =="
ls -R ./dist || true
- name: Download Windows artifact
uses: actions/download-artifact@v4
with:
name: windows
path: ./dist/windows
- name: Download macOS artifact
uses: actions/download-artifact@v4
with:
name: macos
path: ./dist/macos
- name: Download iOS artifacts
uses: actions/download-artifact@v4
with:
name: ios
path: ./dist/ios
- name: Generate release notes
id: notes
run: |
TAG="${{ steps.meta.outputs.tag_name }}"
cat > RELEASE_NOTES.md << 'EOF'
🚀 xplayer Release
附件包含:
- Android: APK 与 AAB
- Windows: xplayer-windows-x64.zip(未签名)
- macOS: xplayer-macos.zip / xplayer-macos.dmg(未签名,首次运行需通过 Gatekeeper)
- iOS: Runner-iphoneos.app.zip 与 Runner-unsigned.ipa(均为未签名,无法直接安装或上架,仅供存档/后续签名)
EOF
echo "notes_file=RELEASE_NOTES.md" >> $GITHUB_OUTPUT
- name: Create Release
if: ${{ steps.meta.outputs.create_release == 'true' }}
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.meta.outputs.tag_name }}
name: ${{ steps.meta.outputs.release_name }}
prerelease: ${{ steps.meta.outputs.prerelease == 'true' }}
body_path: ${{ steps.notes.outputs.notes_file }}
files: |
dist/android/**/*.apk
dist/android/**/*.aab
dist/windows/*
dist/macos/*
dist/ios/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}