mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-14 05:49:02 +02:00
Build the XCFramework through scripts/build_ios.sh and use the framework configuration already committed in the Xcode project. Remove duplicate registration and its dedicated Ruby dependency setup. Verified an unsigned release archive and one Gobackend reference, link entry, and embedded framework.
603 lines
21 KiB
YAML
603 lines
21 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
|
|
env:
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
INPUT_VERSION: ${{ github.event.inputs.version }}
|
|
run: |
|
|
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
|
|
VERSION="$INPUT_VERSION"
|
|
else
|
|
VERSION="${GITHUB_REF#refs/tags/}"
|
|
fi
|
|
if [[ ! "$VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
|
|
echo "Invalid release version" >&2
|
|
exit 1
|
|
fi
|
|
printf 'version=%s\n' "$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: Free disk space
|
|
run: |
|
|
# Remove large unused tools (~15GB total). Docker prune was dropped:
|
|
# it took 1-2 minutes and the rm below already frees enough.
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /opt/ghc
|
|
sudo rm -rf /opt/hostedtoolcache/CodeQL
|
|
sudo rm -rf /usr/local/share/boost
|
|
sudo rm -rf /usr/share/swift
|
|
sudo rm -rf /usr/local/.ghcup
|
|
# Show available space
|
|
df -h
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
|
|
with:
|
|
distribution: "temurin"
|
|
java-version: "25"
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
|
|
with:
|
|
go-version-file: go_backend/go.mod
|
|
cache-dependency-path: go_backend/go.sum
|
|
|
|
# Cache Gradle for faster builds
|
|
- name: Cache Gradle
|
|
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
- name: Cache Android NDK
|
|
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: /usr/local/lib/android/sdk/ndk/29.0.14206865
|
|
key: ndk-29.0.14206865
|
|
|
|
- name: Install Android SDK & NDK
|
|
run: |
|
|
# Use pre-installed Android SDK on GitHub runners
|
|
echo "ANDROID_HOME=$ANDROID_HOME"
|
|
echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT"
|
|
|
|
# Accept licenses
|
|
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses || true
|
|
|
|
# Install NDK r29 (supports 16KB page size for Android 15+)
|
|
# Keep the installed platform aligned with compileSdk/targetSdk.
|
|
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "ndk;29.0.14206865" "platforms;android-37.0" "build-tools;37.0.0"
|
|
|
|
# Set NDK path
|
|
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/29.0.14206865" >> $GITHUB_ENV
|
|
|
|
- name: Install gomobile
|
|
working-directory: go_backend
|
|
run: |
|
|
# Installed from inside the module so the go.mod-pinned x/mobile
|
|
# version is used (reproducible + covered by the setup-go cache).
|
|
go install golang.org/x/mobile/cmd/gomobile
|
|
gomobile init
|
|
|
|
- name: Build Go backend for Android
|
|
working-directory: go_backend
|
|
run: |
|
|
mkdir -p ../android/app/libs
|
|
# arm/arm64 only: ndk.abiFilters in app/build.gradle.kts strips
|
|
# every other ABI from all outputs (universal APK included), so an
|
|
# amd64 slice would never reach a shipped APK — it only bloats the
|
|
# aar and slows this step.
|
|
gomobile bind -target=android/arm,android/arm64 -androidapi 24 -o ../android/app/libs/gobackend.aar .
|
|
env:
|
|
CGO_ENABLED: 1
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
|
|
with:
|
|
channel: "stable"
|
|
flutter-version-file: .fvmrc
|
|
cache: true
|
|
|
|
- name: Cache pub dependencies
|
|
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ~/.pub-cache
|
|
key: pub-${{ runner.os }}-${{ hashFiles('pubspec.lock') }}
|
|
restore-keys: pub-${{ runner.os }}-
|
|
|
|
- name: Get Flutter dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Generate app icons
|
|
run: dart run flutter_launcher_icons
|
|
|
|
- name: Build APK (Release - unsigned)
|
|
run: |
|
|
bash scripts/build_android.sh
|
|
ls -la build/app/outputs/flutter-apk/
|
|
|
|
- name: Sign APKs
|
|
uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # 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: "36.0.0"
|
|
|
|
- name: Rename APKs
|
|
env:
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
run: |
|
|
cd build/app/outputs/flutter-apk
|
|
|
|
rename_required_apk() {
|
|
unsigned="$1"
|
|
destination="$2"
|
|
signed="${unsigned%.apk}-signed.apk"
|
|
if [ -f "$signed" ]; then
|
|
mv "$signed" "$destination"
|
|
else
|
|
echo "ERROR: Missing signed APK: $signed"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
rename_required_apk \
|
|
app-arm64-v8a-release.apk \
|
|
"SpotiFLAC-${VERSION}-arm64.apk"
|
|
rename_required_apk \
|
|
app-armeabi-v7a-release.apk \
|
|
"SpotiFLAC-${VERSION}-arm32.apk"
|
|
|
|
apksigner="$(find "$ANDROID_HOME/build-tools" -type f -name apksigner -print | sort -V | tail -n 1)"
|
|
if [ -z "$apksigner" ]; then
|
|
echo "ERROR: apksigner is unavailable" >&2
|
|
exit 1
|
|
fi
|
|
|
|
for apk in SpotiFLAC-*.apk; do
|
|
"$apksigner" verify --verbose --print-certs "$apk"
|
|
if "$apksigner" verify --print-certs "$apk" | grep -qi "Android Debug"; then
|
|
echo "ERROR: Refusing to publish a debug-signed APK" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
ls -la
|
|
|
|
- name: Upload APK artifact
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
with:
|
|
name: android-apk
|
|
path: build/app/outputs/flutter-apk/SpotiFLAC-*.apk
|
|
|
|
build-ios:
|
|
runs-on: macos-15
|
|
needs: get-version # Only depends on version, NOT android build!
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
|
|
- name: Select Xcode 26.1.1
|
|
run: |
|
|
sudo xcode-select -s /Applications/Xcode_26.1.1.app
|
|
xcodebuild -version
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
|
|
with:
|
|
go-version-file: go_backend/go.mod
|
|
cache-dependency-path: go_backend/go.sum
|
|
|
|
# Cache CocoaPods
|
|
- name: Cache CocoaPods
|
|
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ios/Pods
|
|
key: pods-${{ runner.os }}-${{ hashFiles('ios/Podfile.lock') }}
|
|
restore-keys: pods-${{ runner.os }}-
|
|
|
|
- name: Build Go backend for iOS
|
|
run: bash scripts/build_ios.sh
|
|
env:
|
|
CGO_ENABLED: 1
|
|
|
|
- name: Setup Flutter
|
|
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
|
|
with:
|
|
channel: "stable"
|
|
flutter-version-file: .fvmrc
|
|
cache: true
|
|
|
|
- name: Cache pub dependencies
|
|
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ~/.pub-cache
|
|
key: pub-${{ runner.os }}-${{ hashFiles('pubspec.lock') }}
|
|
restore-keys: pub-${{ runner.os }}-
|
|
|
|
- name: Get Flutter dependencies
|
|
run: flutter pub get
|
|
|
|
- name: Normalize ffmpeg plugin shell scripts (strip CRLF)
|
|
run: |
|
|
find "$HOME/.pub-cache/hosted" -path "*ffmpeg_kit_flutter_new_full*/scripts/*.sh" -type f -print0 |
|
|
while IFS= read -r -d '' f; do
|
|
perl -pi -e 's/\r$//' "$f"
|
|
chmod +x "$f"
|
|
echo "Normalized line endings: $f"
|
|
done
|
|
|
|
- name: Generate app icons
|
|
run: dart run flutter_launcher_icons
|
|
|
|
- name: Build iOS (unsigned)
|
|
run: |
|
|
# Build Flutter iOS without codesigning
|
|
flutter build ios --release --no-codesign --config-only
|
|
|
|
# Use xcodebuild with code signing disabled
|
|
cd ios
|
|
xcodebuild -workspace Runner.xcworkspace \
|
|
-scheme Runner \
|
|
-configuration Release \
|
|
-sdk iphoneos \
|
|
-destination 'generic/platform=iOS' \
|
|
-archivePath build/Runner.xcarchive \
|
|
archive \
|
|
CODE_SIGNING_ALLOWED=NO \
|
|
CODE_SIGNING_REQUIRED=NO \
|
|
CODE_SIGN_IDENTITY="" \
|
|
DEVELOPMENT_TEAM=""
|
|
|
|
- name: Create IPA
|
|
env:
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
run: |
|
|
mkdir -p build/ios/ipa
|
|
cd ios/build/Runner.xcarchive/Products/Applications
|
|
mkdir Payload
|
|
cp -r Runner.app Payload/
|
|
# Use absolute path to avoid relative path issues
|
|
zip -r $GITHUB_WORKSPACE/build/ios/ipa/SpotiFLAC-${VERSION}-ios-unsigned.ipa Payload
|
|
rm -rf Payload
|
|
|
|
- name: Verify IPA created
|
|
env:
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
run: |
|
|
ls -la build/ios/ipa/
|
|
if [ ! -f "build/ios/ipa/SpotiFLAC-${VERSION}-ios-unsigned.ipa" ]; then
|
|
echo "ERROR: IPA not created!"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Upload IPA artifact
|
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
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@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
with:
|
|
fetch-depth: 0 # Full history needed for git-cliff
|
|
|
|
- name: Generate changelog with git-cliff
|
|
id: changelog
|
|
uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4
|
|
with:
|
|
config: cliff.toml
|
|
args: --latest --strip header
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
OUTPUT: /tmp/changelog.txt
|
|
|
|
- name: Show generated changelog
|
|
run: |
|
|
echo "Generated changelog:"
|
|
cat /tmp/changelog.txt
|
|
|
|
- name: Download Android APK
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: android-apk
|
|
path: ./release
|
|
|
|
- name: Download iOS IPA
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: ios-ipa
|
|
path: ./release
|
|
|
|
- name: Prepare release body
|
|
env:
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
REPO_OWNER: ${{ github.repository_owner }}
|
|
REPO_NAME: ${{ github.event.repository.name }}
|
|
run: |
|
|
CURRENT_REF=$(git rev-list -n 1 "$VERSION" 2>/dev/null || git rev-parse HEAD)
|
|
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${CURRENT_REF}^" 2>/dev/null || true)
|
|
|
|
# Start with git-cliff changelog, but replace its compare footer with a
|
|
# deterministic previous-tag lookup from git.
|
|
sed '/^## [0-9][0-9.[:alpha:]-]*$/d; /^\*\*Full Changelog\*\*/d' /tmp/changelog.txt > /tmp/release_body.txt
|
|
|
|
if [ -n "$PREVIOUS_TAG" ]; then
|
|
printf '\n**Full Changelog**: [%s...%s](https://github.com/%s/%s/compare/%s...%s)\n' \
|
|
"$PREVIOUS_TAG" "$VERSION" "$REPO_OWNER" "$REPO_NAME" "$PREVIOUS_TAG" "$VERSION" \
|
|
>> /tmp/release_body.txt
|
|
fi
|
|
|
|
# Append download section
|
|
cat >> /tmp/release_body.txt << FOOTER
|
|
|
|
---
|
|
|
|
### Downloads
|
|
|
|
#### Android
|
|
- **arm64**: \`SpotiFLAC-${VERSION}-arm64.apk\` (recommended for modern devices)
|
|
- **arm32**: \`SpotiFLAC-${VERSION}-arm32.apk\` (older devices)
|
|
|
|
#### iOS
|
|
- **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@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
|
with:
|
|
tag_name: ${{ needs.get-version.outputs.version }}
|
|
name: SpotiFLAC-Mobile ${{ 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 }}
|
|
|
|
update-altstore:
|
|
runs-on: ubuntu-latest
|
|
needs: [get-version, build-ios, create-release]
|
|
if: ${{ needs.get-version.outputs.is_prerelease != 'true' }}
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: Checkout main branch
|
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
with:
|
|
ref: main
|
|
|
|
- name: Download iOS IPA
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: ios-ipa
|
|
path: ./release
|
|
|
|
- name: Update apps.json
|
|
env:
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
run: |
|
|
VERSION_NUM="${VERSION#v}"
|
|
DATE=$(date -u +%Y-%m-%d)
|
|
IPA_FILE=$(find ./release -name "*ios*.ipa" | head -1)
|
|
|
|
if [ -z "$IPA_FILE" ]; then
|
|
echo "WARNING: IPA file not found, skipping apps.json update"
|
|
exit 0
|
|
fi
|
|
|
|
IPA_SIZE=$(stat -c%s "$IPA_FILE" 2>/dev/null || stat -f%z "$IPA_FILE")
|
|
|
|
if [ ! -f apps.json ]; then
|
|
echo "WARNING: apps.json not found on main, skipping"
|
|
exit 0
|
|
fi
|
|
|
|
jq --arg ver "$VERSION_NUM" \
|
|
--arg date "$DATE" \
|
|
--arg url "https://github.com/${REPOSITORY}/releases/download/${VERSION}/SpotiFLAC-${VERSION}-ios-unsigned.ipa" \
|
|
--argjson size "$IPA_SIZE" \
|
|
'.apps[0].version = $ver | .apps[0].versionDate = $date | .apps[0].downloadURL = $url | .apps[0].size = $size' \
|
|
apps.json > apps.json.tmp && mv apps.json.tmp apps.json
|
|
|
|
echo "Updated apps.json:"
|
|
cat apps.json
|
|
|
|
- name: Commit and push
|
|
env:
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
run: |
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git add apps.json
|
|
git diff --cached --quiet && echo "No changes to commit" || \
|
|
(git commit -m "chore: update AltStore source to ${VERSION}" && git push)
|
|
|
|
notify-telegram:
|
|
runs-on: ubuntu-latest
|
|
needs: [get-version, create-release]
|
|
if: ${{ needs.get-version.outputs.is_prerelease != 'true' }}
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Download Android APK
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: android-apk
|
|
path: ./release
|
|
|
|
- name: Download iOS IPA
|
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
|
with:
|
|
name: ios-ipa
|
|
path: ./release
|
|
|
|
- name: Generate changelog with git-cliff for Telegram
|
|
uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4
|
|
with:
|
|
config: cliff.toml
|
|
args: --latest --strip all
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
OUTPUT: /tmp/cliff_tg.txt
|
|
|
|
- name: Convert changelog for Telegram
|
|
id: changelog
|
|
run: |
|
|
if [ ! -s /tmp/cliff_tg.txt ]; then
|
|
echo "See release notes on GitHub for details." > /tmp/changelog.txt
|
|
else
|
|
# Convert Markdown to Telegram HTML
|
|
CHANGELOG=$(cat /tmp/cliff_tg.txt | \
|
|
sed '/^## [0-9][0-9.[:alpha:]-]*$/d' | \
|
|
sed '/^\*\*Full Changelog\*\*/d' | \
|
|
sed 's/ by \[@[^]]*\](https:\/\/github\.com\/[^)]*)//g' | \
|
|
sed 's/ by @[A-Za-z0-9_-]\+//g' | \
|
|
sed 's/\[#\([0-9]*\)\]([^)]*)/#\1/g' | \
|
|
sed 's/\[@\([^]]*\)\]([^)]*)/@\1/g' | \
|
|
sed 's/&/\&/g' | \
|
|
sed 's/</\</g' | \
|
|
sed 's/>/\>/g' | \
|
|
sed 's/\*\*\([^*]*\)\*\*/<b>\1<\/b>/g' | \
|
|
sed 's/^### \(.*\)$/<b>\1<\/b>/g' | \
|
|
sed 's/^## \(.*\)$/<b>\1<\/b>/g' | \
|
|
sed 's/^- /• /g')
|
|
|
|
# Truncate for Telegram 4096 char limit
|
|
CHANGELOG=$(echo "$CHANGELOG" | head -c 2500 | sed '$d')
|
|
echo "$CHANGELOG" > /tmp/changelog.txt
|
|
fi
|
|
|
|
echo "Telegram changelog:"
|
|
cat /tmp/changelog.txt
|
|
|
|
- name: Send to Telegram Channel
|
|
env:
|
|
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
|
TELEGRAM_CHANNEL_ID: ${{ secrets.TELEGRAM_CHANNEL_ID }}
|
|
VERSION: ${{ needs.get-version.outputs.version }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
run: |
|
|
CHANGELOG=$(cat /tmp/changelog.txt)
|
|
|
|
# Find APK files
|
|
ARM64_APK=$(find ./release -name "*arm64*.apk" | head -1)
|
|
ARM32_APK=$(find ./release -name "*arm32*.apk" | head -1)
|
|
|
|
# Prepare message with changelog (HTML format)
|
|
printf '%s\n' \
|
|
"<b>SpotiFLAC Mobile ${VERSION} Released!</b>" \
|
|
"" \
|
|
"<b>What's New:</b>" \
|
|
"${CHANGELOG}" \
|
|
"" \
|
|
"<a href=\"https://github.com/${REPOSITORY}/releases/tag/${VERSION}\">View Release Notes</a>" \
|
|
> /tmp/telegram_message.txt
|
|
|
|
MESSAGE=$(cat /tmp/telegram_message.txt)
|
|
|
|
# Send message first (using HTML parse mode)
|
|
# Use --data-urlencode for proper encoding of special chars (+, &, etc.)
|
|
# Use || true to ensure file uploads continue even if message fails
|
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
|
--data-urlencode "chat_id=${TELEGRAM_CHANNEL_ID}" \
|
|
--data-urlencode "text=${MESSAGE}" \
|
|
--data-urlencode "parse_mode=HTML" \
|
|
--data-urlencode "disable_web_page_preview=true" || true
|
|
|
|
# Upload arm64 APK to channel
|
|
if [ -f "$ARM64_APK" ]; then
|
|
echo "Uploading arm64 APK to Telegram..."
|
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendDocument" \
|
|
-F chat_id="${TELEGRAM_CHANNEL_ID}" \
|
|
-F document=@"${ARM64_APK}" \
|
|
-F caption="SpotiFLAC Mobile ${VERSION} - arm64 (recommended)"
|
|
fi
|
|
|
|
# Upload arm32 APK to channel
|
|
if [ -f "$ARM32_APK" ]; then
|
|
echo "Uploading arm32 APK to Telegram..."
|
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendDocument" \
|
|
-F chat_id="${TELEGRAM_CHANNEL_ID}" \
|
|
-F document=@"${ARM32_APK}" \
|
|
-F caption="SpotiFLAC Mobile ${VERSION} - arm32"
|
|
fi
|
|
|
|
# Upload iOS IPA to channel
|
|
IOS_IPA=$(find ./release -name "*ios*.ipa" | head -1)
|
|
if [ -f "$IOS_IPA" ]; then
|
|
echo "Uploading iOS IPA to Telegram..."
|
|
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendDocument" \
|
|
-F chat_id="${TELEGRAM_CHANNEL_ID}" \
|
|
-F document=@"${IOS_IPA}" \
|
|
-F caption="SpotiFLAC Mobile ${VERSION} - iOS (unsigned, sideload required)"
|
|
fi
|
|
|
|
echo "Telegram notification sent!"
|