release: v0.4.0 #63
Workflow file for this run
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
| # BuSic Release CI — 在推送 v* tag 时构建全平台 release 并发布到 GitHub Releases | |
| # | |
| # 使用方法: | |
| # 1. 更新 pubspec.yaml 中的 version 字段 (如 0.2.0+2) | |
| # 2. git add . && git commit -m "release: v0.2.0" | |
| # 3. git tag v0.2.0 | |
| # 4. git push origin main --tags | |
| # → 自动触发此工作流, 构建全部平台并创建 GitHub Release | |
| name: Release | |
| on: | |
| # 推送 v* tag 时自动触发 | |
| push: | |
| tags: | |
| - 'v*' | |
| # 手动强制触发(跳过 CI 门禁) | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: '要发布的 Tag(如 v0.3.4)' | |
| required: true | |
| type: string | |
| # 确保同时只有一个 release 在跑 | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write # 创建 Release 需要写权限 | |
| env: | |
| FLUTTER_VERSION: '3.29.1' | |
| jobs: | |
| # ───────────────────── 前置检查: 等待 CI 通过 + 解析 tag ───────────── | |
| check: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.resolve-tag.outputs.tag }} | |
| steps: | |
| - name: Resolve tag | |
| id: resolve-tag | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| # push event: GITHUB_REF = refs/tags/v0.3.4 | |
| echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Wait for CI to pass | |
| if: github.event_name != 'workflow_dispatch' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const sha = context.sha; | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const workflowName = 'CI'; | |
| const maxWait = 15 * 60 * 1000; // 最多等 15 分钟 | |
| const interval = 20 * 1000; // 每 20 秒查一次 | |
| const start = Date.now(); | |
| core.info(`Waiting for "${workflowName}" on commit ${sha} ...`); | |
| while (Date.now() - start < maxWait) { | |
| const runs = await github.rest.actions.listWorkflowRunsForRepo({ | |
| owner, repo, | |
| head_sha: sha, | |
| per_page: 10, | |
| }); | |
| const ciRun = runs.data.workflow_runs.find(r => r.name === workflowName); | |
| if (ciRun) { | |
| core.info(`CI status: ${ciRun.status}, conclusion: ${ciRun.conclusion}`); | |
| if (ciRun.status === 'completed') { | |
| if (ciRun.conclusion === 'success') { | |
| core.info('CI passed ✓'); | |
| return; | |
| } | |
| core.setFailed(`CI finished with conclusion: ${ciRun.conclusion}`); | |
| return; | |
| } | |
| } else { | |
| core.info('CI run not found yet, waiting...'); | |
| } | |
| await new Promise(r => setTimeout(r, interval)); | |
| } | |
| core.setFailed('Timed out waiting for CI'); | |
| # ─────────────────────────────── Linux ─────────────────────────────── | |
| build-linux: | |
| needs: check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.check.outputs.tag }} | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clang cmake ninja-build pkg-config \ | |
| libgtk-3-dev liblzma-dev libstdc++-12-dev \ | |
| libayatana-appindicator3-dev | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.FLUTTER_VERSION }} | |
| channel: stable | |
| cache: true | |
| - name: Install dependencies & generate code | |
| run: | | |
| flutter pub get | |
| dart run build_runner build --delete-conflicting-outputs | |
| - name: Build Linux | |
| run: flutter build linux --release | |
| - name: Install Ruby and fpm | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: '3.2' | |
| - name: Install fpm | |
| run: sudo gem install fpm | |
| - name: Prepare package directory | |
| run: | | |
| mkdir -p package/opt/busic | |
| cp -r build/linux/x64/release/bundle/* package/opt/busic/ | |
| cp scripts/linux/postinst.sh package/ | |
| cp scripts/linux/prerm.sh package/ | |
| mkdir -p package/usr/share/icons/hicolor/256x256/apps | |
| cp assets/images/app_icon.png package/usr/share/icons/hicolor/256x256/apps/busic.png | |
| - name: Create .deb package | |
| run: | | |
| cd package | |
| fpm -t deb -s dir \ | |
| -n busic \ | |
| -v ${{ needs.check.outputs.tag }} \ | |
| -p ${{ github.workspace }}/busic-linux-x64.deb \ | |
| --after-install postinst.sh \ | |
| --before-remove prerm.sh \ | |
| --depends libgtk-3-0 \ | |
| --depends libayatana-appindicator3-1 \ | |
| --description "A high-quality cross-platform music player" \ | |
| --category Audio \ | |
| --url "https://github.com/GlowLED/BuSic" \ | |
| --maintainer "BuSic Team" \ | |
| . | |
| - name: Create .rpm package | |
| run: | | |
| cd package | |
| fpm -t rpm -s dir \ | |
| -n busic \ | |
| -v ${{ needs.check.outputs.tag }} \ | |
| -p ${{ github.workspace }}/busic-linux-x64.rpm \ | |
| --after-install postinst.sh \ | |
| --before-remove prerm.sh \ | |
| --depends gtk3 \ | |
| --depends libappindicator-gtk3 \ | |
| --description "A high-quality cross-platform music player" \ | |
| --category Application:Audio \ | |
| --url "https://github.com/GlowLED/BuSic" \ | |
| . | |
| - name: Download appimagetool | |
| run: | | |
| wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage | |
| chmod +x appimagetool-x86_64.AppImage | |
| - name: Create AppImage | |
| run: | | |
| # Prepare AppDir | |
| mkdir -p AppDir/usr/bin | |
| mkdir -p AppDir/usr/share/applications | |
| mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps | |
| # Copy application files | |
| cp -r build/linux/x64/release/bundle/* AppDir/usr/ | |
| # Create symlink for main executable | |
| ln -sf ../lib/busic AppDir/usr/bin/busic | |
| # Copy icon to BOTH locations (root for appimagetool, usr/share for runtime) | |
| cp assets/images/app_icon.png AppDir/busic.png | |
| cp assets/images/app_icon.png AppDir/usr/share/icons/hicolor/256x256/apps/busic.png | |
| # Copy .desktop file to BOTH locations (root for appimagetool, usr/share for runtime) | |
| cp scripts/appimage/AppDir/busic.desktop AppDir/ | |
| cp scripts/appimage/AppDir/busic.desktop AppDir/usr/share/applications/ | |
| # Download AppRun from AppImage community | |
| wget -q -O AppRun https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-x86_64 | |
| chmod +x AppRun | |
| # Build AppImage | |
| ARCH=x86_64 ./appimagetool-x86_64.AppImage --appimage-extract 2>/dev/null | |
| ARCH=x86_64 ./squashfs-root/AppRun AppDir ${{ github.workspace }}/busic-linux-x64.AppImage | |
| - name: Upload Linux packages | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-release | |
| path: | | |
| busic-linux-x64.deb | |
| busic-linux-x64.rpm | |
| busic-linux-x64.AppImage | |
| # ─────────────────────────────── Windows ───────────────────────────── | |
| build-windows: | |
| needs: check | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.check.outputs.tag }} | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.FLUTTER_VERSION }} | |
| channel: stable | |
| cache: true | |
| - name: Install dependencies & generate code | |
| run: | | |
| flutter pub get | |
| dart run build_runner build --delete-conflicting-outputs | |
| - name: Build Windows | |
| run: flutter build windows --release | |
| - name: Package Windows | |
| shell: pwsh | |
| run: | | |
| Compress-Archive -Path build\windows\x64\runner\Release\* ` | |
| -DestinationPath busic-windows-x64.zip | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-release | |
| path: busic-windows-x64.zip | |
| # ─────────────────────────────── macOS ─────────────────────────────── | |
| build-macos: | |
| needs: check | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.check.outputs.tag }} | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.FLUTTER_VERSION }} | |
| channel: stable | |
| cache: true | |
| - name: Install dependencies & generate code | |
| run: | | |
| flutter pub get | |
| dart run build_runner build --delete-conflicting-outputs | |
| - name: Build macOS | |
| run: flutter build macos --release | |
| - name: Package macOS | |
| run: | | |
| cd build/macos/Build/Products/Release | |
| zip -r ${{ github.workspace }}/busic-macos.zip busic.app | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-release | |
| path: busic-macos.zip | |
| # ─────────────────────────────── Android ───────────────────────────── | |
| build-android: | |
| needs: check | |
| runs-on: ubuntu-latest | |
| env: | |
| ANDROID_NDK_VERSION: '27.0.12077973' | |
| ANDROID_CMAKE_VERSION: '3.22.1' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.check.outputs.tag }} | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| # Pre-install NDK & CMake via sdkmanager so Gradle doesn't download | |
| # them on every build (~1 GB NDK + CMake, takes 5-8 min without cache). | |
| - name: Install Android NDK & CMake | |
| run: | | |
| yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \ | |
| "ndk;${{ env.ANDROID_NDK_VERSION }}" \ | |
| "cmake;${{ env.ANDROID_CMAKE_VERSION }}" \ | |
| --sdk_root=$ANDROID_HOME 2>/dev/null || true | |
| # Cache NDK + CMake across runs (keyed by version) | |
| - name: Cache Android NDK & CMake | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.ANDROID_HOME }}/ndk/${{ env.ANDROID_NDK_VERSION }} | |
| ${{ env.ANDROID_HOME }}/cmake/${{ env.ANDROID_CMAKE_VERSION }} | |
| key: android-ndk-${{ env.ANDROID_NDK_VERSION }}-cmake-${{ env.ANDROID_CMAKE_VERSION }} | |
| # Cache Gradle wrapper, dependencies, and build cache | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| gradle-${{ runner.os }}- | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.FLUTTER_VERSION }} | |
| channel: stable | |
| cache: true | |
| - name: Install dependencies & generate code | |
| run: | | |
| flutter pub get | |
| dart run build_runner build --delete-conflicting-outputs | |
| - name: Decode keystore | |
| if: env.KEYSTORE_BASE64 != '' | |
| env: | |
| KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} | |
| run: | | |
| echo "$KEYSTORE_BASE64" | base64 -d > android/upload-keystore.jks | |
| - name: Build APK | |
| env: | |
| KEY_ALIAS: ${{ secrets.KEY_ALIAS }} | |
| KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} | |
| STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }} | |
| run: flutter build apk --release | |
| - name: Rename APK | |
| run: | | |
| cp build/app/outputs/flutter-apk/app-release.apk busic-android.apk | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-release | |
| path: busic-android.apk | |
| # ──────────────────────────────── iOS ──────────────────────────────── | |
| build-ios: | |
| needs: check | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ needs.check.outputs.tag }} | |
| - uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.FLUTTER_VERSION }} | |
| channel: stable | |
| cache: true | |
| - name: Install dependencies & generate code | |
| run: | | |
| flutter pub get | |
| dart run build_runner build --delete-conflicting-outputs | |
| - name: Build iOS (no codesign) | |
| run: flutter build ios --release --no-codesign | |
| - name: Package IPA (unsigned) | |
| run: | | |
| mkdir -p Payload | |
| cp -r build/ios/iphoneos/Runner.app Payload/ | |
| zip -r busic-ios-unsigned.ipa Payload | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-release | |
| path: busic-ios-unsigned.ipa | |
| # ───────────────────────── Publish GitHub Release ──────────────────── | |
| publish: | |
| needs: [check, build-linux, build-windows, build-macos, build-android, build-ios] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: sha256sum busic-* > checksums.sha256 | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.check.outputs.tag }} | |
| generate_release_notes: true | |
| files: | | |
| busic-linux-x64.deb | |
| busic-linux-x64.rpm | |
| busic-linux-x64.AppImage | |
| busic-windows-x64.zip | |
| busic-macos.zip | |
| busic-android.apk | |
| busic-ios-unsigned.ipa | |
| checksums.sha256 |