diff --git a/.env.example b/.env.example
index 22abd24bd..566656632 100644
--- a/.env.example
+++ b/.env.example
@@ -9,3 +9,6 @@ ENABLE_UPDATE_CHECK=
LASTFM_API_KEY=
LASTFM_API_SECRET=
+
+# Release channel. Can be: nightly, stable
+RELEASE_CHANNEL=
diff --git a/.github/Dockerfile b/.github/Dockerfile
index 007d1a6e7..2e3934499 100644
--- a/.github/Dockerfile
+++ b/.github/Dockerfile
@@ -10,14 +10,10 @@ COPY . .
RUN chown -R $(whoami) /app
-RUN flutter pub get &&\
- flutter config --enable-linux-desktop &&\
- flutter pub get &&\
- dart run build_runner build --delete-conflicting-outputs
+RUN flutter pub get
RUN alias dpkg-deb="dpkg-deb --Zxz" &&\
- flutter_distributor package --platform=linux --targets=deb
-
+ flutter_distributor package --platform=linux --targets=deb --skip-clean
RUN make tar VERSION=${BUILD_VERSION} ARCH=arm64 PKG_ARCH=aarch64
diff --git a/.github/workflows/spotube-release-binary.yml b/.github/workflows/spotube-release-binary.yml
index cabe2dbf6..0fe1f1bac 100644
--- a/.github/workflows/spotube-release-binary.yml
+++ b/.github/workflows/spotube-release-binary.yml
@@ -2,296 +2,65 @@ name: Spotube Release Binary
on:
workflow_dispatch:
inputs:
- version:
- description: Version to release (x.x.x)
- default: 3.6.0
- required: true
channel:
type: choice
- description: Release Channel
- required: true
options:
- stable
- nightly
default: nightly
+ description: The release channel
debug:
- description: Debug on failed when channel is nightly
- required: true
type: boolean
default: false
+ description: Debug with SSH toggle
+ required: false
dry_run:
- description: Dry run
- required: true
type: boolean
- default: true
+ default: false
+ description: Dry run without uploading to release
env:
- FLUTTER_VERSION: '3.19.5'
-
-jobs:
- windows:
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v4
- - uses: subosito/flutter-action@v2.12.0
- with:
- cache: true
- flutter-version: ${{ env.FLUTTER_VERSION }}
-
- - name: Replace pubspec version and BUILD_VERSION Env (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- choco install sed make yq -y
- yq -i '.version |= sub("\+\d+", "-${{ inputs.channel }}+")' pubspec.yaml
- yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
- echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
- shell: bash
-
- - name: BUILD_VERSION Env (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- "BUILD_VERSION=${{ inputs.version }}" >> $env:GITHUB_ENV
-
- - name: Replace version in files
- run: |
- choco install sed make -y
- sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.BUILD_VERSION }}/" windows/runner/Runner.rc
- sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.BUILD_VERSION }}/" choco-struct/tools/VERIFICATION.txt
- sed -i "s/%{{SPOTUBE_VERSION}}%/${{ env.BUILD_VERSION }}/" choco-struct/spotube.nuspec
-
- - name: Create Stable .env
- if: ${{ inputs.channel == 'stable' }}
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
-
- - name: Create Nightly .env
- if: ${{ inputs.channel == 'nightly' }}
- run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
-
- - name: Generating Secrets
- run: |
- flutter config --enable-windows-desktop
- flutter pub get
- dart run build_runner build --delete-conflicting-outputs
-
- - name: Build Windows Executable
- run: |
- dart pub global activate flutter_distributor
- make innoinstall
- flutter_distributor package --platform=windows --targets=exe --skip-clean
- mv dist/**/spotube-*-windows-setup.exe dist/Spotube-windows-x86_64-setup.exe
-
- - name: Create Chocolatey Package and set hash
- if: ${{ inputs.channel == 'stable' }}
- run: |
- Set-Variable -Name HASH -Value (Get-FileHash dist\Spotube-windows-x86_64-setup.exe).Hash
- sed -i "s/%{{WIN_SHA256}}%/$HASH/" choco-struct/tools/VERIFICATION.txt
- make choco
- mv dist/spotube.*.nupkg dist/Spotube-windows-x86_64.nupkg
-
-
- - name: Upload Artifact
- uses: actions/upload-artifact@v3
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- dist/Spotube-windows-x86_64.nupkg
- dist/Spotube-windows-x86_64-setup.exe
-
- - name: Debug With SSH When fails
- if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
- uses: mxschmitt/action-tmate@v3
- with:
- limit-access-to-actor: true
-
- linux:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: subosito/flutter-action@v2.12.0
- with:
- cache: true
- flutter-version: ${{ env.FLUTTER_VERSION }}
-
- - name: Get current date
- id: date
- run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
-
- - name: Install Dependencies
- run: |
- sudo apt-get update -y
- sudo apt-get install -y tar clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev
-
- - name: Install AppImage Tool
- run: |
- wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
- chmod +x appimagetool
- mv appimagetool /usr/local/bin/
-
- - name: Replace pubspec version and BUILD_VERSION Env (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- curl -sS https://webi.sh/yq | sh
- yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
- yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
- echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
-
- - name: BUILD_VERSION Env (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
-
- - name: Create Stable .env
- if: ${{ inputs.channel == 'stable' }}
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
-
- - name: Create Nightly .env
- if: ${{ inputs.channel == 'nightly' }}
- run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
-
- - name: Replace Version in files
- run: |
- sed -i 's|%{{APPDATA_RELEASE}}%||' linux/com.github.KRTirtho.Spotube.appdata.xml
-
- - name: Generate Secrets
- run: |
- flutter config --enable-linux-desktop
- flutter pub get
- dart run build_runner build --delete-conflicting-outputs
-
- - name: Build Linux Packages
- run: |
- dart pub global activate flutter_distributor
- alias dpkg-deb="dpkg-deb --Zxz"
- flutter_distributor package --platform=linux --targets=deb
- flutter_distributor package --platform=linux --targets=rpm
-
- - name: Create tar.xz (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: make tar VERSION=${{ env.BUILD_VERSION }} ARCH=x64 PKG_ARCH=x86_64
-
- - name: Create tar.xz (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: make tar VERSION=nightly ARCH=x64 PKG_ARCH=x86_64
-
- - name: Move Files to dist
- run: |
- mv build/spotube-linux-*-x86_64.tar.xz dist/
- mv dist/**/spotube-*-linux.deb dist/Spotube-linux-x86_64.deb
- mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-x86_64.rpm
-
-
- - uses: actions/upload-artifact@v3
- if: ${{ inputs.channel == 'stable' }}
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- dist/Spotube-linux-x86_64.deb
- dist/Spotube-linux-x86_64.rpm
- dist/spotube-linux-${{ env.BUILD_VERSION }}-x86_64.tar.xz
-
- - uses: actions/upload-artifact@v3
- if: ${{ inputs.channel == 'nightly' }}
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- dist/Spotube-linux-x86_64.deb
- dist/Spotube-linux-x86_64.rpm
- dist/spotube-linux-nightly-x86_64.tar.xz
-
- - name: Debug With SSH When fails
- if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
- uses: mxschmitt/action-tmate@v3
- with:
- limit-access-to-actor: true
-
- linux_arm:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v3
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- - name: Get current date
- id: date
- run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
-
- - name: Install Dependencies
- run: |
- sudo apt-get update -y
- sudo apt-get install -y pkg-config make python3-pip python3-setuptools
-
- - name: Replace pubspec version and BUILD_VERSION Env (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- curl -sS https://webi.sh/yq | sh
- yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
- yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
- echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
-
- - name: BUILD_VERSION Env (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
-
- - name: Create Stable .env
- if: ${{ inputs.channel == 'stable' }}
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
-
- - name: Create Nightly .env
- if: ${{ inputs.channel == 'nightly' }}
- run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
-
- - name: Replace Version in files
- run: |
- sed -i 's|%{{APPDATA_RELEASE}}%||' linux/com.github.KRTirtho.Spotube.appdata.xml
-
- - name: Build Binaries (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- docker buildx build --platform=linux/arm64 -f .github/Dockerfile . --build-arg FLUTTER_VERSION=${{env.FLUTTER_VERSION}} --build-arg BUILD_VERSION=${{env.BUILD_VERSION}} -t krtirtho/spotube_linux_arm:latest --load
-
- - name: Build Binaries (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- docker buildx build --platform=linux/arm64 -f .github/Dockerfile . --build-arg FLUTTER_VERSION=${{env.FLUTTER_VERSION}} --build-arg BUILD_VERSION=nightly -t krtirtho/spotube_linux_arm:latest --load
-
- - name: Copy the built packages
- run: |
- docker images ls
- docker create --name spotube_linux_arm krtirtho/spotube_linux_arm:latest
- docker cp spotube_linux_arm:/app/dist/ dist/
+ FLUTTER_VERSION: 3.19.5
- - uses: actions/upload-artifact@v3
- if: ${{ inputs.channel == 'stable' }}
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- dist/Spotube-linux-aarch64.deb
- dist/spotube-linux-${{ env.BUILD_VERSION }}-aarch64.tar.xz
-
- - uses: actions/upload-artifact@v3
- if: ${{ inputs.channel == 'nightly' }}
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- dist/Spotube-linux-aarch64.deb
- dist/spotube-linux-nightly-aarch64.tar.xz
+permissions:
+ contents: write
- - name: Debug With SSH When fails
- if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
- uses: mxschmitt/action-tmate@v3
- with:
- limit-access-to-actor: true
-
- android:
- runs-on: ubuntu-latest
+jobs:
+ build_platform:
+ strategy:
+ matrix:
+ include:
+ - os: ubuntu-latest
+ platform: linux
+ files: |
+ dist/Spotube-linux-x86_64.deb
+ dist/Spotube-linux-x86_64.rpm
+ dist/spotube-linux-*-x86_64.tar.xz
+ - os: ubuntu-latest
+ platform: linux_arm
+ files: |
+ dist/Spotube-linux-aarch64.deb
+ dist/spotube-linux-*-aarch64.tar.xz
+ - os: ubuntu-latest
+ platform: android
+ files: |
+ build/Spotube-android-all-arch.apk
+ build/Spotube-playstore-all-arch.aab
+ - os: windows-latest
+ platform: windows
+ files: |
+ dist/Spotube-windows-x86_64.nupkg
+ dist/Spotube-windows-x86_64-setup.exe
+ - os: macos-latest
+ platform: ios
+ files: |
+ Spotube-iOS.ipa
+ - os: macos-14
+ platform: macos
+ files: |
+ build/Spotube-macos-universal.dmg
+ build/Spotube-macos-universal.pkg
+ runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2.12.0
@@ -299,137 +68,42 @@ jobs:
cache: true
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Setup Java
+ if: ${{matrix.platform == 'android'}}
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
cache: 'gradle'
check-latest: true
+ - name: Set up QEMU
+ if: ${{matrix.platform == 'linux_arm'}}
+ uses: docker/setup-qemu-action@v3
+ - name: Set up Docker Buildx
+ if: ${{matrix.platform == 'linux_arm'}}
+ uses: docker/setup-buildx-action@v3
- - name: Install Dependencies
- run: |
- sudo apt-get update -y
- sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse xmlstarlet
-
- - name: Replace pubspec version and BUILD_VERSION Env (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- curl -sS https://webi.sh/yq | sh
- yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
- yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
- echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
-
- - name: BUILD_VERSION Env (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
-
- - name: Create Stable .env
- if: ${{ inputs.channel == 'stable' }}
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
-
- - name: Create Nightly .env
- if: ${{ inputs.channel == 'nightly' }}
- run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
-
- - name: Generate Secrets
+ - name: Install ${{matrix.platform}} dependencies
run: |
flutter pub get
- dart run build_runner build --delete-conflicting-outputs
+ dart cli/cli.dart install-dependencies --platform=${{matrix.platform}}
- name: Sign Apk
+ if: ${{matrix.platform == 'android'}}
run: |
echo '${{ secrets.KEYSTORE }}' | base64 --decode > android/app/upload-keystore.jks
echo '${{ secrets.KEY_PROPERTIES }}' > android/key.properties
-
- - name: Build Apk
- run: |
- flutter build apk --flavor ${{ inputs.channel }}
- mv build/app/outputs/flutter-apk/app-${{ inputs.channel }}-release.apk build/Spotube-android-all-arch.apk
-
- - name: Build Playstore AppBundle
- run: |
- echo 'ENABLE_UPDATE_CHECK=0' >> .env
- dart run build_runner build --delete-conflicting-outputs
- export MANIFEST=android/app/src/main/AndroidManifest.xml
- xmlstarlet ed -d '//meta-data[@android:name="com.google.android.gms.car.application"]' $MANIFEST > $MANIFEST.tmp
- mv $MANIFEST.tmp $MANIFEST
- flutter build appbundle --flavor ${{ inputs.channel }}
- mv build/app/outputs/bundle/${{ inputs.channel }}Release/app-${{ inputs.channel }}-release.aab build/Spotube-playstore-all-arch.aab
-
-
- - uses: actions/upload-artifact@v3
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- build/Spotube-android-all-arch.apk
- build/Spotube-playstore-all-arch.aab
-
- - name: Debug With SSH When fails
- if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
- uses: mxschmitt/action-tmate@v3
- with:
- limit-access-to-actor: true
-
- macos:
- runs-on: macos-14
- steps:
- - uses: actions/checkout@v4
- - uses: subosito/flutter-action@v2.12.0
- with:
- cache: true
- flutter-version: ${{ env.FLUTTER_VERSION }}
-
- - name: Replace pubspec version and BUILD_VERSION Env (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- brew install yq
- yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
- yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
- echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
-
- - name: BUILD_VERSION Env (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
-
- - name: Create Stable .env
- if: ${{ inputs.channel == 'stable' }}
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
-
- - name: Create Nightly .env
- if: ${{ inputs.channel == 'nightly' }}
- run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
-
- - name: Generate Secrets
- run: |
- dart pub global activate flutter_distributor
- flutter pub get
- dart run build_runner build --delete-conflicting-outputs
-
- - name: Build Macos App
- run: |
- flutter config --enable-macos-desktop
- flutter build macos
- du -sh build/macos/Build/Products/Release/spotube.app
-
- - name: Package Macos App
- run: |
- brew install python-setuptools
- npm install -g appdmg
- mkdir -p build/${{ env.BUILD_VERSION }}
- appdmg appdmg.json build/Spotube-macos-universal.dmg
- flutter_distributor package --platform=macos --targets pkg --skip-clean
- mv dist/**/spotube-*-macos.pkg build/Spotube-macos-universal.pkg
-
+
+ - name: Build ${{matrix.platform}} binaries
+ run: dart cli/cli.dart build ${{matrix.platform}}
+ env:
+ CHANNEL: ${{inputs.channel}}
+ DOTENV: ${{secrets.DOTENV_RELEASE}}
+
- uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Spotube-Release-Binaries
- path: |
- build/Spotube-macos-universal.dmg
- build/Spotube-macos-universal.pkg
+ path: ${{matrix.files}}
- name: Debug With SSH When fails
if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
@@ -437,70 +111,10 @@ jobs:
with:
limit-access-to-actor: true
- iOS:
- runs-on: macos-14
- steps:
- - uses: actions/checkout@v4
- - uses: subosito/flutter-action@v2.10.0
- with:
- cache: true
- flutter-version: ${{ env.FLUTTER_VERSION }}
-
- - name: Replace pubspec version and BUILD_VERSION Env (nightly)
- if: ${{ inputs.channel == 'nightly' }}
- run: |
- brew install yq
- yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
- yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
- echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
-
- - name: BUILD_VERSION Env (stable)
- if: ${{ inputs.channel == 'stable' }}
- run: |
- echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
-
- - name: Create Stable .env
- if: ${{ inputs.channel == 'stable' }}
- run: echo '${{ secrets.DOTENV_RELEASE }}' > .env
-
- - name: Create Nightly .env
- if: ${{ inputs.channel == 'nightly' }}
- run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
-
- - name: Generate Secrets
- run: |
- flutter pub get
- dart run build_runner build --delete-conflicting-outputs
-
- - name: Build iOS iPA
- run: |
- flutter build ios --release --no-codesign --flavor ${{ inputs.channel }}
- ln -sf ./build/ios/iphoneos Payload
- zip -r9 Spotube-iOS.ipa Payload/${{ inputs.channel }}.app
-
- - uses: actions/upload-artifact@v3
- with:
- if-no-files-found: error
- name: Spotube-Release-Binaries
- path: |
- Spotube-iOS.ipa
-
- - name: Debug With SSH When fails
- if: ${{ failure() && inputs.debug && inputs.channel == 'nightly' }}
- uses: mxschmitt/action-tmate@v3
- with:
- limit-access-to-actor: true
-
upload:
runs-on: ubuntu-latest
-
needs:
- - windows
- - linux
- - linux_arm
- - android
- - macos
- - iOS
+ - build_platform
steps:
- uses: actions/download-artifact@v3
with:
@@ -516,6 +130,10 @@ jobs:
md5sum Spotube-Release-Binaries/* >> RELEASE.md5sum
sha256sum Spotube-Release-Binaries/* >> RELEASE.sha256sum
sed -i 's|Spotube-Release-Binaries/||' RELEASE.sha256sum RELEASE.md5sum
+
+ - name: Extract pubspec version
+ run: |
+ echo "PUBSPEC_VERSION=$(grep -oP 'version:\s*\K[^+]+(?=\+)' pubspec.yaml)" >> $GITHUB_ENV
- uses: actions/upload-artifact@v3
with:
@@ -530,7 +148,7 @@ jobs:
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- tag: v${{ inputs.version }} # mind the "v" prefix
+ tag: v${{ env.PUBSPEC_VERSION }} # mind the "v" prefix
omitBodyDuringUpdate: true
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
@@ -548,3 +166,8 @@ jobs:
omitPrereleaseDuringUpdate: true
allowUpdates: true
artifacts: Spotube-Release-Binaries/*,RELEASE.sha256sum,RELEASE.md5sum
+ body: |
+ Build Number: ${{github.run_number}}
+
+ Nightly release includes newest features but may contain bugs
+ It is preferred to use the stable version unless you know what you're doing
diff --git a/.metadata b/.metadata
index 082985ad4..828f2c0ad 100644
--- a/.metadata
+++ b/.metadata
@@ -1,11 +1,11 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
-# This file should be version controlled.
+# This file should be version controlled and should not be manually edited.
version:
- revision: eb6d86ee27deecba4a83536aa20f366a6044895c
- channel: stable
+ revision: "300451adae589accbece3490f4396f10bdf15e6e"
+ channel: "stable"
project_type: app
@@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
- create_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
- base_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
- - platform: macos
- create_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
- base_revision: eb6d86ee27deecba4a83536aa20f366a6044895c
+ create_revision: 300451adae589accbece3490f4396f10bdf15e6e
+ base_revision: 300451adae589accbece3490f4396f10bdf15e6e
+ - platform: windows
+ create_revision: 300451adae589accbece3490f4396f10bdf15e6e
+ base_revision: 300451adae589accbece3490f4396f10bdf15e6e
# User provided section
diff --git a/cli/README.md b/cli/README.md
new file mode 100644
index 000000000..b2ba8ebda
--- /dev/null
+++ b/cli/README.md
@@ -0,0 +1,4 @@
+## Spotube Configuration CLI
+
+This is used for building the project for multiple platforms and having utilities specific for the project.
+Written in Dart
diff --git a/cli/cli.dart b/cli/cli.dart
new file mode 100644
index 000000000..3210f5574
--- /dev/null
+++ b/cli/cli.dart
@@ -0,0 +1,16 @@
+import 'package:args/command_runner.dart';
+
+import 'commands/build.dart';
+import 'commands/install-dependencies.dart';
+
+void main(List args) {
+ final commandRunner = CommandRunner(
+ "cli",
+ "Configuration CLI for Spotube",
+ );
+
+ commandRunner.addCommand(InstallDependenciesCommand());
+ commandRunner.addCommand(BuildCommand());
+
+ commandRunner.run(args);
+}
diff --git a/cli/commands/build.dart b/cli/commands/build.dart
new file mode 100644
index 000000000..fdf35a952
--- /dev/null
+++ b/cli/commands/build.dart
@@ -0,0 +1,25 @@
+import 'package:args/command_runner.dart';
+
+import 'build/android.dart';
+import 'build/ios.dart';
+import 'build/linux.dart';
+import 'build/linux_arm.dart';
+import 'build/macos.dart';
+import 'build/windows.dart';
+
+class BuildCommand extends Command {
+ @override
+ String get description => "Build for different platforms";
+
+ @override
+ String get name => "build";
+
+ BuildCommand() {
+ addSubcommand(AndroidBuildCommand());
+ addSubcommand(IosBuildCommand());
+ addSubcommand(LinuxBuildCommand());
+ addSubcommand(LinuxArmBuildCommand());
+ addSubcommand(MacosBuildCommand());
+ addSubcommand(WindowsBuildCommand());
+ }
+}
diff --git a/cli/commands/build/android.dart b/cli/commands/build/android.dart
new file mode 100644
index 000000000..800522b8a
--- /dev/null
+++ b/cli/commands/build/android.dart
@@ -0,0 +1,90 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/command_runner.dart';
+import 'package:collection/collection.dart';
+import 'package:path/path.dart';
+import 'package:xml/xml.dart';
+
+import '../../core/env.dart';
+import 'common.dart';
+
+class AndroidBuildCommand extends Command with BuildCommandCommonSteps {
+ @override
+ String get description => "Build for android";
+
+ @override
+ String get name => "android";
+
+ @override
+ FutureOr? run() async {
+ await bootstrap();
+
+ await shell.run(
+ "flutter build apk --flavor ${CliEnv.channel.name}",
+ );
+
+ await dotEnvFile.writeAsString(
+ "\nENABLE_UPDATE_CHECK=0",
+ mode: FileMode.append,
+ );
+
+ final androidManifestFile = File(
+ join(cwd.path, "android", "app", "src", "main", "AndroidManifest.xml"));
+
+ final androidManifestXml =
+ XmlDocument.parse(await androidManifestFile.readAsString());
+
+ final deletingElement =
+ androidManifestXml.findAllElements("meta-data").firstWhereOrNull(
+ (el) =>
+ el.getAttribute("android:name") ==
+ "com.google.android.gms.car.application",
+ );
+
+ deletingElement?.parent?.children.remove(deletingElement);
+
+ await androidManifestFile.writeAsString(
+ androidManifestXml.toXmlString(pretty: true),
+ );
+
+ await shell.run(
+ """
+ dart run build_runner build --delete-conflicting-outputs
+ flutter build appbundle --flavor ${CliEnv.channel.name}
+ """,
+ );
+
+ final ogApkFile = File(
+ join(
+ "build",
+ "app",
+ "outputs",
+ "flutter-apk",
+ "app-${CliEnv.channel.name}-release.apk",
+ ),
+ );
+
+ await ogApkFile.copy(
+ join(cwd.path, "build", "Spotube-android-all-arch.apk"),
+ );
+
+ final ogAppbundleFile = File(
+ join(
+ cwd.path,
+ "build",
+ "app",
+ "outputs",
+ "bundle",
+ "${CliEnv.channel.name}Release",
+ "app-${CliEnv.channel.name}-release.aab",
+ ),
+ );
+
+ await ogAppbundleFile.copy(
+ join(cwd.path, "build", "Spotube-playstore-all-arch.aab"),
+ );
+
+ stdout.writeln("✅ Built Android Apk and Appbundle");
+ }
+}
diff --git a/cli/commands/build/common.dart b/cli/commands/build/common.dart
new file mode 100644
index 000000000..4c7e3e510
--- /dev/null
+++ b/cli/commands/build/common.dart
@@ -0,0 +1,66 @@
+import 'dart:io';
+
+import 'package:args/command_runner.dart';
+import 'package:path/path.dart';
+import 'package:process_run/shell_run.dart';
+import 'package:pubspec_parse/pubspec_parse.dart';
+
+import '../../core/env.dart';
+
+mixin BuildCommandCommonSteps on Command {
+ final shell = Shell();
+ Directory get cwd => Directory.current;
+
+ Pubspec? _pubspec;
+
+ Pubspec get pubspec {
+ if (_pubspec != null) {
+ return _pubspec!;
+ }
+
+ final pubspecFile = File(join(cwd.path, "pubspec.yaml"));
+ _pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
+
+ return _pubspec!;
+ }
+
+ String get versionWithoutBuildNumber {
+ return "${pubspec.version!.major}.${pubspec.version!.minor}.${pubspec.version!.patch}";
+ }
+
+ RegExp get versionVarRegExp =>
+ RegExp(r"\%\{\{SPOTUBE_VERSION\}\}\%", multiLine: true);
+
+ File get dotEnvFile => File(join(cwd.path, ".env"));
+
+ Future bootstrap() async {
+ await dotEnvFile.create(recursive: true);
+
+ await dotEnvFile.writeAsString(
+ "${CliEnv.dotenv}\n"
+ "RELEASE_CHANNEL=${CliEnv.channel.name}\n",
+ );
+
+ if (CliEnv.channel == BuildChannel.nightly) {
+ final pubspecFile = File(join(cwd.path, "pubspec.yaml"));
+
+ pubspecFile.writeAsStringSync(
+ pubspecFile.readAsStringSync().replaceAll(
+ "version: ${pubspec.version!.canonicalizedVersion}",
+ "version: $versionWithoutBuildNumber+${CliEnv.ghRunNumber}",
+ ),
+ );
+
+ _pubspec = null;
+ pubspec;
+ }
+
+ await shell.run(
+ """
+ flutter pub get
+ dart run build_runner build --delete-conflicting-outputs
+ dart pub global activate flutter_distributor
+ """,
+ );
+ }
+}
diff --git a/cli/commands/build/ios.dart b/cli/commands/build/ios.dart
new file mode 100644
index 000000000..6460f9edb
--- /dev/null
+++ b/cli/commands/build/ios.dart
@@ -0,0 +1,29 @@
+import 'dart:async';
+
+import 'package:args/command_runner.dart';
+import 'package:path/path.dart';
+
+import '../../core/env.dart';
+import 'common.dart';
+
+class IosBuildCommand extends Command with BuildCommandCommonSteps {
+ @override
+ String get description => "iOS build command";
+
+ @override
+ String get name => "ios";
+
+ @override
+ FutureOr? run() async {
+ await bootstrap();
+
+ final buildDirPath = join(cwd.path, "build", "ios", "iphoneos");
+ await shell.run(
+ """
+ flutter build ios --release --no-codesign --flavor ${CliEnv.channel.name}
+ ln -sf $buildDirPath Payload
+ zip -r9 Spotube-iOS.ipa ${join("Payload", "${CliEnv.channel.name}.app")}
+ """,
+ );
+ }
+}
diff --git a/cli/commands/build/linux.dart b/cli/commands/build/linux.dart
new file mode 100644
index 000000000..a218720ce
--- /dev/null
+++ b/cli/commands/build/linux.dart
@@ -0,0 +1,106 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:io/io.dart';
+import 'package:args/command_runner.dart';
+import 'package:intl/intl.dart';
+import 'package:path/path.dart';
+
+import '../../core/env.dart';
+import 'common.dart';
+
+class LinuxBuildCommand extends Command with BuildCommandCommonSteps {
+ @override
+ String get description => "Linux build command";
+
+ @override
+ String get name => "linux";
+
+ @override
+ FutureOr? run() async {
+ stdout.writeln("Replacing versions");
+
+ final appDataFile = File(
+ join(cwd.path, "linux", "com.github.KRTirtho.Spotube.appdata.xml"),
+ );
+
+ appDataFile.writeAsStringSync(
+ appDataFile.readAsStringSync().replaceAll(
+ versionVarRegExp,
+ '',
+ ),
+ );
+
+ await bootstrap();
+
+ await shell.run(
+ """
+ flutter_distributor package --platform=linux --targets=deb
+ flutter_distributor package --platform=linux --targets=rpm
+ """,
+ );
+
+ final tempDir = join(Directory.systemTemp.path, "spotube-tar");
+
+ final bundleDirPath =
+ join(cwd.path, "build", "linux", "x64", "release", "bundle");
+
+ final tarFile = File(join(
+ cwd.path,
+ "dist",
+ "spotube-linux-"
+ "${CliEnv.channel == BuildChannel.nightly ? "nightly" : versionWithoutBuildNumber}"
+ "-x86_64.tar.xz",
+ ));
+
+ await copyPath(bundleDirPath, tempDir);
+ await File(join(cwd.path, "linux", "spotube.desktop")).copy(
+ join(tempDir, "spotube.desktop"),
+ );
+ await File(
+ join(cwd.path, "linux", "com.github.KRTirtho.Spotube.appdata.xml"),
+ ).copy(
+ join(tempDir, "com.github.KRTirtho.Spotube.appdata.xml"),
+ );
+ await File(join(cwd.path, "assets", "spotube-logo.png")).copy(
+ join(tempDir, "spotube-logo.png"),
+ );
+
+ await shell.run(
+ "tar -cJf ${tarFile.path} -C $tempDir .",
+ );
+
+ final ogDeb = File(
+ join(
+ cwd.path,
+ "dist",
+ pubspec.version.toString(),
+ "spotube-${pubspec.version}-linux.deb",
+ ),
+ );
+
+ final ogRpm = File(
+ join(
+ cwd.path,
+ "dist",
+ pubspec.version.toString(),
+ "spotube-${pubspec.version}-linux.rpm",
+ ),
+ );
+
+ await ogDeb.copy(
+ join(cwd.path, "dist", "Spotube-linux-x86_64.deb"),
+ );
+ await ogRpm.copy(
+ join(cwd.path, "dist", "Spotube-linux-x86_64.rpm"),
+ );
+
+ await ogDeb.delete();
+ await ogRpm.delete();
+
+ stdout.writeln("✅ Linux building done");
+ }
+}
diff --git a/cli/commands/build/linux_arm.dart b/cli/commands/build/linux_arm.dart
new file mode 100644
index 000000000..a09f09808
--- /dev/null
+++ b/cli/commands/build/linux_arm.dart
@@ -0,0 +1,37 @@
+import 'dart:async';
+
+import 'package:args/command_runner.dart';
+import 'package:path/path.dart';
+
+import '../../core/env.dart';
+import 'common.dart';
+
+class LinuxArmBuildCommand extends Command with BuildCommandCommonSteps {
+ @override
+ String get description => "Build Linux Arm";
+
+ @override
+ String get name => "linux_arm";
+
+ @override
+ FutureOr? run() async {
+ await bootstrap();
+
+ await shell.run(
+ "docker buildx build --platform=linux/arm64 "
+ "-f ${join(cwd.path, ".github", "Dockerfile")} ${cwd.path} "
+ "--build-arg FLUTTER_VERSION=${CliEnv.flutterVersion} "
+ "--build-arg BUILD_VERSION=${CliEnv.channel == BuildChannel.nightly ? "nightly" : versionWithoutBuildNumber} "
+ "-t krtirtho/spotube_linux_arm:latest "
+ "--load",
+ );
+
+ await shell.run(
+ """
+ docker images ls
+ docker create --name spotube_linux_arm krtirtho/spotube_linux_arm:latest
+ docker cp spotube_linux_arm:/app/dist/ dist/
+ """,
+ );
+ }
+}
diff --git a/cli/commands/build/macos.dart b/cli/commands/build/macos.dart
new file mode 100644
index 000000000..e8f34b775
--- /dev/null
+++ b/cli/commands/build/macos.dart
@@ -0,0 +1,42 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/command_runner.dart';
+import 'package:path/path.dart';
+
+import 'common.dart';
+
+class MacosBuildCommand extends Command with BuildCommandCommonSteps {
+ @override
+ String get description => "Macos Build command";
+
+ @override
+ String get name => "macos";
+
+ @override
+ FutureOr? run() async {
+ await bootstrap();
+
+ await shell.run(
+ """
+ flutter build macos
+ appdmg appdmg.json ${join(cwd.path, "build", "Spotube-macos-universal.dmg")}
+ flutter_distributor package --platform=macos --targets pkg --skip-clean
+ """,
+ );
+
+ final ogPkg = File(
+ join(
+ cwd.path,
+ "dist",
+ pubspec.version.toString(),
+ "spotube-${pubspec.version}-macos.pkg",
+ ),
+ );
+
+ await ogPkg.copy(
+ join(cwd.path, "build", "Spotube-macos-universal.pkg"),
+ );
+ await ogPkg.delete();
+ }
+}
diff --git a/cli/commands/build/windows.dart b/cli/commands/build/windows.dart
new file mode 100644
index 000000000..15e0bf170
--- /dev/null
+++ b/cli/commands/build/windows.dart
@@ -0,0 +1,100 @@
+import 'dart:io';
+
+import 'package:args/command_runner.dart';
+import 'package:path/path.dart';
+import 'package:crypto/crypto.dart';
+import 'common.dart';
+
+class WindowsBuildCommand extends Command with BuildCommandCommonSteps {
+ @override
+ String get description => "Build Windows exe";
+
+ @override
+ String get name => "windows";
+
+ Future innoDependInstall() async {
+ final innoDependencyPath = join(cwd.path, "build", "inno-depend");
+
+ await shell.run(
+ "git clone https://github.com/DomGries/InnoDependencyInstaller.git $innoDependencyPath",
+ );
+ }
+
+ @override
+ void run() async {
+ stdout.writeln("Replace versions");
+
+ final chocoFiles = [
+ join(cwd.path, "choco-struct", "tools", "VERIFICATION.txt"),
+ join(cwd.path, "choco-struct", "spotube.nuspec"),
+ ];
+
+ for (final filePath in chocoFiles) {
+ final file = File(filePath);
+ final content = file.readAsStringSync();
+ final newContent =
+ content.replaceAll(versionVarRegExp, versionWithoutBuildNumber);
+
+ file.writeAsStringSync(newContent);
+ }
+
+ await bootstrap();
+ await innoDependInstall();
+
+ await shell.run(
+ "flutter_distributor package --platform=windows --targets=exe --skip-clean",
+ );
+
+ final ogExe = File(
+ join(
+ cwd.path,
+ "dist",
+ pubspec.version.toString(),
+ "spotube-${pubspec.version}-windows-setup.exe",
+ ),
+ );
+
+ final exePath = join(cwd.path, "dist", "Spotube-windows-x86_64-setup.exe");
+
+ await ogExe.copy(exePath);
+ await ogExe.delete();
+
+ stdout.writeln("✅ Windows exe built at $exePath");
+
+ final exeFile = File(exePath);
+
+ final hash = sha256.convert(await exeFile.readAsBytes()).toString();
+
+ final chocoVerificationFile = File(chocoFiles.first);
+
+ chocoVerificationFile.writeAsStringSync(
+ chocoVerificationFile.readAsStringSync().replaceAll(
+ RegExp(r"\%\{\{WIN_SHA256\}\}\%"),
+ hash,
+ ),
+ );
+
+ await exeFile.copy(
+ join(cwd.path, "choco-struct", "tools", basename(exeFile.path)),
+ );
+
+ await shell.run(
+ "choco pack ${chocoFiles[1]} --outputdirectory ${join(cwd.path, "dist")}",
+ );
+
+ final chocoNupkg = File(
+ join(cwd.path, "dist", "spotube.$versionWithoutBuildNumber.nupkg"),
+ );
+
+ final distNupkgPath = join(
+ cwd.path,
+ "dist",
+ "Spotube-windows-x86_64.nupkg",
+ );
+
+ await chocoNupkg.copy(distNupkgPath);
+ await chocoNupkg.delete();
+
+ stdout.writeln("✅ Windows nupkg built at $distNupkgPath");
+ }
+}
diff --git a/cli/commands/install-dependencies.dart b/cli/commands/install-dependencies.dart
new file mode 100644
index 000000000..75df28dfa
--- /dev/null
+++ b/cli/commands/install-dependencies.dart
@@ -0,0 +1,74 @@
+import 'dart:async';
+
+import 'package:args/command_runner.dart';
+import 'package:process_run/shell_run.dart';
+
+class InstallDependenciesCommand extends Command {
+ @override
+ String get description => "Install platform dependencies";
+
+ @override
+ String get name => "install-dependencies";
+
+ InstallDependenciesCommand() {
+ argParser.addOption(
+ "platform",
+ abbr: "p",
+ allowed: [
+ "windows",
+ "linux",
+ "linux_arm",
+ "macos",
+ "ios",
+ "android",
+ ],
+ mandatory: true,
+ );
+ }
+
+ @override
+ FutureOr? run() async {
+ final shell = Shell();
+
+ switch (argResults!.option("platform")) {
+ case "windows":
+ break;
+ case "linux":
+ await shell.run(
+ """
+ sudo apt-get update -y
+ sudo apt-get install -y tar clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev
+ """,
+ );
+ break;
+ case "linux_arm":
+ await shell.run(
+ """
+ sudo apt-get update -y
+ sudo apt-get install -y pkg-config make python3-pip python3-setuptools
+ """,
+ );
+ break;
+ case "macos":
+ await shell.run(
+ """
+ brew install python-setuptools
+ npm install -g appdmg
+ """,
+ );
+ break;
+ case "ios":
+ break;
+ case "android":
+ await shell.run(
+ """
+ sudo apt-get update -y
+ sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse
+ """,
+ );
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/cli/core/env.dart b/cli/core/env.dart
new file mode 100644
index 000000000..33cc5df13
--- /dev/null
+++ b/cli/core/env.dart
@@ -0,0 +1,24 @@
+import 'dart:io';
+
+enum BuildChannel {
+ stable,
+ nightly;
+
+ factory BuildChannel.fromEnvironment(String name) {
+ final channel = Platform.environment[name]!;
+ if (channel == "stable") {
+ return BuildChannel.stable;
+ } else if (channel == "nightly") {
+ return BuildChannel.nightly;
+ } else {
+ throw Exception("Invalid channel: $channel");
+ }
+ }
+}
+
+class CliEnv {
+ static final channel = BuildChannel.fromEnvironment("CHANNEL");
+ static final dotenv = Platform.environment["DOTENV"]!;
+ static final ghRunNumber = Platform.environment["GITHUB_RUN_NUMBER"];
+ static final flutterVersion = Platform.environment["FLUTTER_VERSION"]!;
+}
diff --git a/lib/collections/env.dart b/lib/collections/env.dart
index 14f33b806..89a777b64 100644
--- a/lib/collections/env.dart
+++ b/lib/collections/env.dart
@@ -3,6 +3,11 @@ import 'package:spotube/utils/platform.dart';
part 'env.g.dart';
+enum ReleaseChannel {
+ nightly,
+ stable,
+}
+
@Envied(obfuscate: true, requireEnvFile: true, path: ".env")
abstract class Env {
@EnviedField(varName: 'SPOTIFY_SECRETS')
@@ -25,6 +30,13 @@ abstract class Env {
@EnviedField(varName: 'ENABLE_UPDATE_CHECK', defaultValue: "1")
static final String _enableUpdateChecker = _Env._enableUpdateChecker;
+ @EnviedField(varName: "RELEASE_CHANNEL", defaultValue: "nightly")
+ static final String _releaseChannel = _Env._releaseChannel;
+
+ static ReleaseChannel get releaseChannel => _releaseChannel == "stable"
+ ? ReleaseChannel.stable
+ : ReleaseChannel.nightly;
+
static bool get enableUpdateChecker =>
kIsFlatpak || _enableUpdateChecker == "1";
diff --git a/lib/components/root/update_dialog.dart b/lib/components/root/update_dialog.dart
index f5388aa1f..e15903c63 100644
--- a/lib/components/root/update_dialog.dart
+++ b/lib/components/root/update_dialog.dart
@@ -5,18 +5,23 @@ import 'package:version/version.dart';
class RootAppUpdateDialog extends StatelessWidget {
final Version? version;
- const RootAppUpdateDialog({super.key, this.version});
+ final int? nightlyBuildNum;
+
+ const RootAppUpdateDialog({super.key, this.version}) : nightlyBuildNum = null;
+ const RootAppUpdateDialog.nightly({super.key, required this.nightlyBuildNum})
+ : version = null;
@override
Widget build(BuildContext context) {
const url = "https://spotube.krtirtho.dev/downloads";
+ const nightlyUrl = "https://spotube.krtirtho.dev/downloads/nightly";
return AlertDialog(
title: const Text("Spotube has an update"),
actions: [
FilledButton(
child: const Text("Download Now"),
onPressed: () => launchUrlString(
- url,
+ nightlyBuildNum != null ? nightlyUrl : url,
mode: LaunchMode.externalApplication,
),
),
@@ -24,21 +29,26 @@ class RootAppUpdateDialog extends StatelessWidget {
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
- Text("Spotube v$version has been released"),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const Text("Read the latest "),
- AnchorButton(
- "release notes",
- style: const TextStyle(color: Colors.blue),
- onTap: () => launchUrlString(
- url,
- mode: LaunchMode.externalApplication,
- ),
- ),
- ],
+ Text(
+ nightlyBuildNum != null
+ ? "Spotube Nightly $nightlyBuildNum has been released"
+ : "Spotube v$version has been released",
),
+ if (nightlyBuildNum == null)
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Text("Read the latest "),
+ AnchorButton(
+ "release notes",
+ style: const TextStyle(color: Colors.blue),
+ onTap: () => launchUrlString(
+ url,
+ mode: LaunchMode.externalApplication,
+ ),
+ ),
+ ],
+ ),
],
),
);
diff --git a/lib/pages/settings/about.dart b/lib/pages/settings/about.dart
index 21b8117b1..505eecb94 100644
--- a/lib/pages/settings/about.dart
+++ b/lib/pages/settings/about.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:spotube/collections/assets.gen.dart';
+import 'package:spotube/collections/env.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/links/hyper_link.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
@@ -72,6 +73,13 @@ class AboutSpotube extends HookConsumerWidget {
Text("v${packageInfo.version}")
],
),
+ TableRow(
+ children: [
+ Text(context.l10n.channel),
+ colon,
+ Text(Env.releaseChannel.name)
+ ],
+ ),
TableRow(
children: [
Text(context.l10n.build_number),
diff --git a/lib/utils/service_utils.dart b/lib/utils/service_utils.dart
index 30c92e1d7..ec3bb0cbf 100644
--- a/lib/utils/service_utils.dart
+++ b/lib/utils/service_utils.dart
@@ -335,35 +335,59 @@ abstract class ServiceUtils {
) async {
if (!Env.enableUpdateChecker) return;
if (!ref.read(userPreferencesProvider.select((s) => s.checkUpdate))) return;
-
final packageInfo = await PackageInfo.fromPlatform();
- final value = await http.get(
- Uri.parse(
- "https://api.github.com/repos/KRTirtho/spotube/releases/latest",
- ),
- );
- final tagName =
- (jsonDecode(value.body)["tag_name"] as String).replaceAll("v", "");
- final currentVersion = packageInfo.version == "Unknown"
- ? null
- : Version.parse(packageInfo.version);
- final latestVersion = tagName == "nightly" ? null : Version.parse(tagName);
-
- if (currentVersion == null ||
- latestVersion == null ||
- (latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
- (!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
-
- if (latestVersion <= currentVersion || !context.mounted) return;
-
- showDialog(
- context: context,
- barrierDismissible: true,
- barrierColor: Colors.black26,
- builder: (context) {
- return RootAppUpdateDialog(version: latestVersion);
- },
- );
+ if (Env.releaseChannel == ReleaseChannel.nightly) {
+ final value = await http.get(
+ Uri.parse(
+ "https://api.github.com/repos/KRTirtho/spotube/actions/workflows/spotube-release-binary.yml/runs?status=success&per_page=1",
+ ),
+ );
+
+ final buildNum =
+ jsonDecode(value.body)["workflow_runs"][0]["run_number"] as int;
+
+ if (buildNum <= int.parse(packageInfo.buildNumber) || !context.mounted) {
+ return;
+ }
+
+ await showDialog(
+ context: context,
+ barrierDismissible: true,
+ barrierColor: Colors.black26,
+ builder: (context) {
+ return RootAppUpdateDialog.nightly(nightlyBuildNum: buildNum);
+ },
+ );
+ } else {
+ final value = await http.get(
+ Uri.parse(
+ "https://api.github.com/repos/KRTirtho/spotube/releases/latest",
+ ),
+ );
+ final tagName =
+ (jsonDecode(value.body)["tag_name"] as String).replaceAll("v", "");
+ final currentVersion = packageInfo.version == "Unknown"
+ ? null
+ : Version.parse(packageInfo.version);
+ final latestVersion =
+ tagName == "nightly" ? null : Version.parse(tagName);
+
+ if (currentVersion == null ||
+ latestVersion == null ||
+ (latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
+ (!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
+
+ if (latestVersion <= currentVersion || !context.mounted) return;
+
+ showDialog(
+ context: context,
+ barrierDismissible: true,
+ barrierColor: Colors.black26,
+ builder: (context) {
+ return RootAppUpdateDialog(version: latestVersion);
+ },
+ );
+ }
}
}
diff --git a/pubspec.lock b/pubspec.lock
index 1532bcf75..df623b9e1 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -45,10 +45,10 @@ packages:
dependency: transitive
description:
name: archive
- sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
+ sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
url: "https://pub.dev"
source: hosted
- version: "3.4.10"
+ version: "3.5.1"
args:
dependency: "direct main"
description:
@@ -1271,7 +1271,7 @@ packages:
source: hosted
version: "3.1.14"
io:
- dependency: transitive
+ dependency: "direct dev"
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
@@ -1702,14 +1702,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
- pointycastle:
- dependency: transitive
- description:
- name: pointycastle
- sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333"
- url: "https://pub.dev"
- source: hosted
- version: "3.8.0"
pool:
dependency: transitive
description:
@@ -1734,6 +1726,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.2"
+ process_run:
+ dependency: "direct dev"
+ description:
+ name: process_run
+ sha256: "8d9c6198b98fbbfb511edd42e7364e24d85c163e47398919871b952dc86a423e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.14.2"
provider:
dependency: transitive
description:
@@ -2462,7 +2462,7 @@ packages:
source: hosted
version: "1.0.4"
xml:
- dependency: transitive
+ dependency: "direct dev"
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
diff --git a/pubspec.yaml b/pubspec.yaml
index 62c20c354..7435e077c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -144,6 +144,9 @@ dev_dependencies:
freezed: ^2.5.2
custom_lint: ^0.6.4
riverpod_lint: ^2.3.10
+ process_run: ^0.14.2
+ xml: ^6.5.0
+ io: ^1.0.4
dependency_overrides:
uuid: ^4.4.0
diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt
index 6e1e3cb37..0c638eb75 100644
--- a/windows/CMakeLists.txt
+++ b/windows/CMakeLists.txt
@@ -1,13 +1,16 @@
+# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(spotube LANGUAGES CXX)
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
set(BINARY_NAME "spotube")
-cmake_policy(SET CMP0063 NEW)
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(VERSION 3.14...3.25)
-set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
-
-# Configure build options.
+# Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
@@ -20,7 +23,7 @@ else()
"Debug" "Profile" "Release")
endif()
endif()
-
+# Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
@@ -30,6 +33,10 @@ set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
@@ -38,14 +45,14 @@ function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
endfunction()
-set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
-
# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
-# Application build
+# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
+
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
@@ -80,6 +87,12 @@ if(PLUGIN_BUNDLED_LIBRARIES)
COMPONENT Runtime)
endif()
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/")
+install(DIRECTORY "${NATIVE_ASSETS_DIR}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc
index e8fccc8a6..0b586d339 100644
--- a/windows/runner/Runner.rc
+++ b/windows/runner/Runner.rc
@@ -60,16 +60,16 @@ IDI_APP_ICON ICON "resources\\app_icon.ico"
// Version
//
-#ifdef FLUTTER_BUILD_NUMBER
-#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
+#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
-#define VERSION_AS_NUMBER 1,0,0
+#define VERSION_AS_NUMBER 1,0,0,0
#endif
-#ifdef FLUTTER_BUILD_NAME
-#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#if defined(FLUTTER_VERSION)
+#define VERSION_AS_STRING FLUTTER_VERSION
#else
-#define VERSION_AS_STRING "%{{SPOTUBE_VERSION}}%"
+#define VERSION_AS_STRING "3.6.0"
#endif
VS_VERSION_INFO VERSIONINFO
@@ -93,7 +93,7 @@ BEGIN
VALUE "FileDescription", "Spotube" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "spotube" "\0"
- VALUE "LegalCopyright", "Copyright (C) 2022 oss.krtirtho. All rights reserved." "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2024 oss.krtirtho. All rights reserved." "\0"
VALUE "OriginalFilename", "spotube.exe" "\0"
VALUE "ProductName", "spotube" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"