mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-04-01 01:20:21 +02:00
- Quality picker now shows track name, artist, and cover - Tap to expand long track titles (icon only shows when truncated) - Ripple effect follows rounded corners including drag handle - Update dialog redesigned with Material Expressive 3 style - Fixed update notification stuck at 100% after download complete - Ask before download now enabled by default - Finalizing notification for multi-progress polling
334 lines
11 KiB
YAML
334 lines
11 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Version tag (e.g., v1.0.0)'
|
|
required: true
|
|
default: 'v1.0.0'
|
|
|
|
jobs:
|
|
# Get version first (quick job)
|
|
get-version:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.version.outputs.version }}
|
|
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
|
|
steps:
|
|
- name: Get version
|
|
id: version
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
VERSION="${{ github.event.inputs.version }}"
|
|
else
|
|
VERSION="${GITHUB_REF#refs/tags/}"
|
|
fi
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
|
|
# Check if version contains -preview, -beta, -rc, or -alpha (NOT -hotfix)
|
|
VERSION_LOWER=$(echo "$VERSION" | tr '[:upper:]' '[:lower:]')
|
|
if [[ "$VERSION_LOWER" == *"-preview"* ]] || [[ "$VERSION_LOWER" == *"-beta"* ]] || [[ "$VERSION_LOWER" == *"-rc"* ]] || [[ "$VERSION_LOWER" == *"-alpha"* ]]; then
|
|
echo "is_prerelease=true" >> $GITHUB_OUTPUT
|
|
echo "Detected pre-release version: $VERSION"
|
|
else
|
|
echo "is_prerelease=false" >> $GITHUB_OUTPUT
|
|
echo "Detected stable version: $VERSION"
|
|
fi
|
|
|
|
# Android and iOS build in PARALLEL
|
|
build-android:
|
|
runs-on: ubuntu-latest
|
|
needs: get-version
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: 'temurin'
|
|
java-version: '17'
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.21'
|
|
cache-dependency-path: go_backend/go.sum
|
|
|
|
# Cache Gradle for faster builds
|
|
- 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 }}-
|
|
|
|
- name: Install Android SDK & NDK
|
|
uses: android-actions/setup-android@v3
|
|
|
|
- name: Install gomobile
|
|
run: |
|
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
|
gomobile init
|
|
|
|
- name: Build Go backend for Android
|
|
working-directory: go_backend
|
|
run: |
|
|
mkdir -p ../android/app/libs
|
|
gomobile bind -target=android -androidapi 24 -o ../android/app/libs/gobackend.aar .
|
|
env:
|
|
CGO_ENABLED: 1
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: 'stable'
|
|
cache: true
|
|
|
|
- name: Get Flutter dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Generate app icons
|
|
run: dart run flutter_launcher_icons
|
|
|
|
- name: Build APK (Release - unsigned)
|
|
run: flutter build apk --release --split-per-abi
|
|
|
|
- name: Sign APKs
|
|
uses: r0adkll/sign-android-release@v1
|
|
id: sign_arm64
|
|
with:
|
|
releaseDirectory: build/app/outputs/flutter-apk
|
|
signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
|
alias: ${{ secrets.KEY_ALIAS }}
|
|
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
keyPassword: ${{ secrets.KEY_PASSWORD }}
|
|
env:
|
|
BUILD_TOOLS_VERSION: "34.0.0"
|
|
|
|
- name: Rename APKs
|
|
run: |
|
|
VERSION=${{ needs.get-version.outputs.version }}
|
|
cd build/app/outputs/flutter-apk
|
|
# Signed files have -signed suffix
|
|
mv app-arm64-v8a-release-signed.apk SpotiFLAC-${VERSION}-arm64.apk || mv app-arm64-v8a-release.apk SpotiFLAC-${VERSION}-arm64.apk || true
|
|
mv app-armeabi-v7a-release-signed.apk SpotiFLAC-${VERSION}-arm32.apk || mv app-armeabi-v7a-release.apk SpotiFLAC-${VERSION}-arm32.apk || true
|
|
mv app-release-signed.apk SpotiFLAC-${VERSION}-universal.apk || mv app-release.apk SpotiFLAC-${VERSION}-universal.apk || true
|
|
ls -la
|
|
|
|
- name: Upload APK artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: android-apk
|
|
path: build/app/outputs/flutter-apk/SpotiFLAC-*.apk
|
|
|
|
build-ios:
|
|
runs-on: macos-latest
|
|
needs: get-version # Only depends on version, NOT android build!
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.21'
|
|
cache-dependency-path: go_backend/go.sum
|
|
|
|
# Cache CocoaPods
|
|
- name: Cache CocoaPods
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ios/Pods
|
|
key: pods-${{ runner.os }}-${{ hashFiles('ios/Podfile.lock') }}
|
|
restore-keys: pods-${{ runner.os }}-
|
|
|
|
- name: Install gomobile
|
|
run: |
|
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
|
gomobile init
|
|
|
|
- name: Build Go backend for iOS
|
|
working-directory: go_backend
|
|
run: |
|
|
mkdir -p ../ios/Frameworks
|
|
gomobile bind -target=ios -o ../ios/Frameworks/Gobackend.xcframework .
|
|
env:
|
|
CGO_ENABLED: 1
|
|
|
|
- name: Verify XCFramework created
|
|
run: |
|
|
ls -la ios/Frameworks/
|
|
ls -la ios/Frameworks/Gobackend.xcframework/ || (echo "ERROR: XCFramework not found!" && exit 1)
|
|
|
|
- name: Add XCFramework to Xcode project
|
|
run: |
|
|
# Install xcodeproj gem for modifying Xcode project
|
|
sudo gem install xcodeproj
|
|
|
|
# Create Ruby script to add framework
|
|
cat > add_framework.rb << 'EOF'
|
|
require 'xcodeproj'
|
|
|
|
project_path = 'ios/Runner.xcodeproj'
|
|
project = Xcodeproj::Project.open(project_path)
|
|
|
|
# Get the main target
|
|
target = project.targets.find { |t| t.name == 'Runner' }
|
|
|
|
# Get or create Frameworks group
|
|
frameworks_group = project.main_group.find_subpath('Frameworks', true)
|
|
frameworks_group ||= project.main_group.new_group('Frameworks')
|
|
|
|
# Add XCFramework reference
|
|
framework_path = 'Frameworks/Gobackend.xcframework'
|
|
framework_ref = frameworks_group.new_file(framework_path, :project)
|
|
|
|
# Add to frameworks build phase
|
|
frameworks_build_phase = target.frameworks_build_phase
|
|
frameworks_build_phase.add_file_reference(framework_ref)
|
|
|
|
# Add to embed frameworks build phase
|
|
embed_phase = target.build_phases.find { |p| p.is_a?(Xcodeproj::Project::Object::PBXCopyFilesBuildPhase) && p.name == 'Embed Frameworks' }
|
|
if embed_phase
|
|
build_file = embed_phase.add_file_reference(framework_ref)
|
|
build_file.settings = { 'ATTRIBUTES' => ['CodeSignOnCopy', 'RemoveHeadersOnCopy'] }
|
|
end
|
|
|
|
project.save
|
|
puts "Successfully added Gobackend.xcframework to Xcode project"
|
|
EOF
|
|
|
|
ruby add_framework.rb
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@v2
|
|
with:
|
|
channel: 'stable'
|
|
cache: true
|
|
|
|
- name: Get Flutter dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Generate app icons
|
|
run: dart run flutter_launcher_icons
|
|
|
|
- name: Build iOS (unsigned)
|
|
run: flutter build ios --release --no-codesign
|
|
|
|
- name: Create IPA
|
|
run: |
|
|
VERSION=${{ needs.get-version.outputs.version }}
|
|
mkdir -p build/ios/ipa
|
|
cd build/ios/iphoneos
|
|
mkdir Payload
|
|
cp -r Runner.app Payload/
|
|
zip -r ../ipa/SpotiFLAC-${VERSION}-ios-unsigned.ipa Payload
|
|
rm -rf Payload
|
|
|
|
- name: Upload IPA artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ios-ipa
|
|
path: build/ios/ipa/SpotiFLAC-*.ipa
|
|
|
|
create-release:
|
|
runs-on: ubuntu-latest
|
|
needs: [get-version, build-android, build-ios]
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Extract changelog for version
|
|
id: changelog
|
|
run: |
|
|
VERSION=${{ needs.get-version.outputs.version }}
|
|
VERSION_NUM=${VERSION#v} # Remove 'v' prefix
|
|
|
|
echo "Looking for version: $VERSION_NUM"
|
|
|
|
# Extract changelog section for this version using sed
|
|
# Find the line with version, then print until next version header or end
|
|
CHANGELOG=$(sed -n "/^## \[$VERSION_NUM\]/,/^## \[/{ /^## \[$VERSION_NUM\]/d; /^## \[/d; p; }" CHANGELOG.md)
|
|
|
|
# If no changelog found, use default message
|
|
if [ -z "$CHANGELOG" ]; then
|
|
echo "No changelog found for version $VERSION_NUM"
|
|
CHANGELOG="See CHANGELOG.md for details."
|
|
else
|
|
echo "Found changelog content"
|
|
fi
|
|
|
|
# Save to file for multiline support
|
|
echo "$CHANGELOG" > /tmp/changelog.txt
|
|
echo "Extracted changelog:"
|
|
cat /tmp/changelog.txt
|
|
|
|
- name: Download Android APK
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: android-apk
|
|
path: ./release
|
|
|
|
- name: Download iOS IPA
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: ios-ipa
|
|
path: ./release
|
|
|
|
- name: Prepare release body
|
|
run: |
|
|
VERSION=${{ needs.get-version.outputs.version }}
|
|
cat > /tmp/release_body.txt << 'HEADER'
|
|
## SpotiFLAC $VERSION
|
|
|
|
Download Spotify tracks in FLAC quality from Tidal, Qobuz & Amazon Music.
|
|
|
|
### What's New
|
|
HEADER
|
|
|
|
# Replace $VERSION in header
|
|
sed -i "s/\$VERSION/$VERSION/g" /tmp/release_body.txt
|
|
|
|
cat /tmp/changelog.txt >> /tmp/release_body.txt
|
|
|
|
cat >> /tmp/release_body.txt << FOOTER
|
|
|
|
---
|
|
|
|
### Downloads
|
|
- **Android (arm64)**: \`SpotiFLAC-${VERSION}-arm64.apk\` (recommended)
|
|
- **Android (arm32)**: \`SpotiFLAC-${VERSION}-arm32.apk\` (older devices)
|
|
- **iOS**: \`SpotiFLAC-${VERSION}-ios-unsigned.ipa\` (sideload required)
|
|
|
|
### Installation
|
|
**Android**: Enable "Install from unknown sources" and install the APK
|
|
**iOS**: Use AltStore, Sideloadly, or similar tools to sideload the IPA
|
|
FOOTER
|
|
|
|
echo "Release body:"
|
|
cat /tmp/release_body.txt
|
|
|
|
- name: Create Release
|
|
uses: softprops/action-gh-release@v1
|
|
with:
|
|
tag_name: ${{ needs.get-version.outputs.version }}
|
|
name: SpotiFLAC ${{ needs.get-version.outputs.version }}
|
|
body_path: /tmp/release_body.txt
|
|
files: ./release/*
|
|
draft: false
|
|
prerelease: ${{ needs.get-version.outputs.is_prerelease == 'true' }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|