diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml
index 72210c87..9acac0f2 100644
--- a/.github/workflows/auto-tag.yml
+++ b/.github/workflows/auto-tag.yml
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
fetch-depth: 2 # Need previous commit to compare
@@ -23,7 +23,11 @@ jobs:
id: current
run: |
VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //' | cut -d'+' -f1)
- echo "version=$VERSION" >> $GITHUB_OUTPUT
+ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
+ echo "Invalid current version" >&2
+ exit 1
+ fi
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Current version: $VERSION"
- name: Get previous version
@@ -36,40 +40,52 @@ jobs:
VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //' | cut -d'+' -f1)
fi
git checkout HEAD -- pubspec.yaml
- echo "version=$VERSION" >> $GITHUB_OUTPUT
+ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
+ echo "Invalid previous version" >&2
+ exit 1
+ fi
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Previous version: $VERSION"
- name: Check if version changed
id: check
+ env:
+ CURRENT: ${{ steps.current.outputs.version }}
+ PREVIOUS: ${{ steps.previous.outputs.version }}
run: |
- CURRENT="${{ steps.current.outputs.version }}"
- PREVIOUS="${{ steps.previous.outputs.version }}"
-
+ if [[ ! "$CURRENT" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
+ echo "Invalid current version" >&2
+ exit 1
+ fi
if [ "$CURRENT" != "$PREVIOUS" ]; then
echo "Version changed from $PREVIOUS to $CURRENT"
- echo "changed=true" >> $GITHUB_OUTPUT
+ echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "Version unchanged: $CURRENT"
- echo "changed=false" >> $GITHUB_OUTPUT
+ echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Check if tag exists
id: tag_exists
if: steps.check.outputs.changed == 'true'
+ env:
+ CURRENT_VERSION: ${{ steps.current.outputs.version }}
run: |
- TAG="v${{ steps.current.outputs.version }}"
+ TAG="v${CURRENT_VERSION}"
if git ls-remote --tags origin | grep -q "refs/tags/$TAG"; then
echo "Tag $TAG already exists"
- echo "exists=true" >> $GITHUB_OUTPUT
+ echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "Tag $TAG does not exist"
- echo "exists=false" >> $GITHUB_OUTPUT
+ echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Create and push tag
if: steps.check.outputs.changed == 'true' && steps.tag_exists.outputs.exists == 'false'
+ env:
+ CURRENT_VERSION: ${{ steps.current.outputs.version }}
run: |
- TAG="v${{ steps.current.outputs.version }}"
+ TAG="v${CURRENT_VERSION}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 918cfb2b..db02c1f9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,10 +22,10 @@ jobs:
android: ${{ steps.filter.outputs.android }}
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Filter paths
- uses: dorny/paths-filter@v3
+ uses: dorny/paths-filter@0e4a8c6effa4802afeda77dc8d303f8176d7dfad # v3
id: filter
with:
filters: |
@@ -63,17 +63,17 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Setup Flutter
- uses: subosito/flutter-action@v2
+ uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version-file: .fvmrc
cache: true
- name: Cache pub dependencies
- uses: actions/cache@v5
+ uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('pubspec.lock') }}
@@ -100,10 +100,10 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Setup Go
- uses: actions/setup-go@v6
+ uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: go_backend/go.mod
cache-dependency-path: go_backend/go.sum
@@ -125,29 +125,29 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Setup Java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: "temurin"
java-version: "25"
- name: Setup Go
- uses: actions/setup-go@v6
+ uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: go_backend/go.mod
cache-dependency-path: go_backend/go.sum
- name: Setup Flutter
- uses: subosito/flutter-action@v2
+ uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version-file: .fvmrc
cache: true
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v5
+ uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5
with:
gradle-version: "9.7.1"
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 6048d59c..a748a382 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -22,13 +22,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Setup Pages
- uses: actions/configure-pages@v5
+ uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
- name: Upload artifact
- uses: actions/upload-pages-artifact@v4
+ uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
with:
path: site
@@ -41,4 +41,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
- uses: actions/deploy-pages@v4
+ uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7e4da4fe..d155c3c1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,21 +21,28 @@ jobs:
steps:
- name: Get version
id: version
+ env:
+ EVENT_NAME: ${{ github.event_name }}
+ INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- VERSION="${{ github.event.inputs.version }}"
+ if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
+ VERSION="$INPUT_VERSION"
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
- echo "version=$VERSION" >> $GITHUB_OUTPUT
+ 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 "is_prerelease=true" >> "$GITHUB_OUTPUT"
echo "Detected pre-release version: $VERSION"
else
- echo "is_prerelease=false" >> $GITHUB_OUTPUT
+ echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
echo "Detected stable version: $VERSION"
fi
@@ -59,23 +66,23 @@ jobs:
df -h
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Setup Java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
with:
distribution: "temurin"
java-version: "25"
- name: Setup Go
- uses: actions/setup-go@v6
+ 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@v5
+ uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
~/.gradle/caches
@@ -84,7 +91,7 @@ jobs:
restore-keys: gradle-${{ runner.os }}-
- name: Cache Android NDK
- uses: actions/cache@v5
+ uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: /usr/local/lib/android/sdk/ndk/29.0.14206865
key: ndk-29.0.14206865
@@ -126,14 +133,14 @@ jobs:
CGO_ENABLED: 1
- name: Setup Flutter
- uses: subosito/flutter-action@v2
+ uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version-file: .fvmrc
cache: true
- name: Cache pub dependencies
- uses: actions/cache@v5
+ uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('pubspec.lock') }}
@@ -154,7 +161,7 @@ jobs:
test -f build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk
- name: Sign APKs
- uses: r0adkll/sign-android-release@v1
+ uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
id: sign_arm64
with:
releaseDirectory: build/app/outputs/flutter-apk
@@ -166,8 +173,9 @@ jobs:
BUILD_TOOLS_VERSION: "36.0.0"
- name: Rename APKs
+ env:
+ VERSION: ${{ needs.get-version.outputs.version }}
run: |
- VERSION="${{ needs.get-version.outputs.version }}"
cd build/app/outputs/flutter-apk
rename_required_apk() {
@@ -176,10 +184,8 @@ jobs:
signed="${unsigned%.apk}-signed.apk"
if [ -f "$signed" ]; then
mv "$signed" "$destination"
- elif [ -f "$unsigned" ]; then
- mv "$unsigned" "$destination"
else
- echo "ERROR: Missing APK: $unsigned"
+ echo "ERROR: Missing signed APK: $signed"
exit 1
fi
}
@@ -190,10 +196,24 @@ jobs:
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@v6
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: android-apk
path: build/app/outputs/flutter-apk/SpotiFLAC-*.apk
@@ -204,7 +224,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Select Xcode 26.1.1
run: |
@@ -212,14 +232,14 @@ jobs:
xcodebuild -version
- name: Setup Go
- uses: actions/setup-go@v6
+ 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@v5
+ uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: ios/Pods
key: pods-${{ runner.os }}-${{ hashFiles('ios/Podfile.lock') }}
@@ -287,14 +307,14 @@ jobs:
ruby add_framework.rb
- name: Setup Flutter
- uses: subosito/flutter-action@v2
+ uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
with:
channel: "stable"
flutter-version-file: .fvmrc
cache: true
- name: Cache pub dependencies
- uses: actions/cache@v5
+ uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: ~/.pub-cache
key: pub-${{ runner.os }}-${{ hashFiles('pubspec.lock') }}
@@ -335,8 +355,9 @@ jobs:
DEVELOPMENT_TEAM=""
- name: Create IPA
+ env:
+ VERSION: ${{ needs.get-version.outputs.version }}
run: |
- VERSION=${{ needs.get-version.outputs.version }}
mkdir -p build/ios/ipa
cd ios/build/Runner.xcarchive/Products/Applications
mkdir Payload
@@ -346,16 +367,17 @@ jobs:
rm -rf Payload
- name: Verify IPA created
+ env:
+ VERSION: ${{ needs.get-version.outputs.version }}
run: |
ls -la build/ios/ipa/
- VERSION=${{ needs.get-version.outputs.version }}
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@v6
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: ios-ipa
path: build/ios/ipa/SpotiFLAC-*.ipa
@@ -368,13 +390,13 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ 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@v4
+ uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4
with:
config: cliff.toml
args: --latest --strip header
@@ -388,22 +410,23 @@ jobs:
cat /tmp/changelog.txt
- name: Download Android APK
- uses: actions/download-artifact@v7
+ uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: android-apk
path: ./release
- name: Download iOS IPA
- uses: actions/download-artifact@v7
+ 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: |
- VERSION=${{ needs.get-version.outputs.version }}
- REPO_OWNER="${{ github.repository_owner }}"
- REPO_NAME="${{ github.event.repository.name }}"
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)
@@ -440,7 +463,7 @@ jobs:
cat /tmp/release_body.txt
- name: Create Release
- uses: softprops/action-gh-release@v2
+ uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ needs.get-version.outputs.version }}
name: SpotiFLAC-Mobile ${{ needs.get-version.outputs.version }}
@@ -460,19 +483,21 @@ jobs:
steps:
- name: Checkout main branch
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: main
- name: Download iOS IPA
- uses: actions/download-artifact@v7
+ 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="${{ needs.get-version.outputs.version }}"
VERSION_NUM="${VERSION#v}"
DATE=$(date -u +%Y-%m-%d)
IPA_FILE=$(find ./release -name "*ios*.ipa" | head -1)
@@ -491,7 +516,7 @@ jobs:
jq --arg ver "$VERSION_NUM" \
--arg date "$DATE" \
- --arg url "https://github.com/${{ github.repository }}/releases/download/${VERSION}/SpotiFLAC-${VERSION}-ios-unsigned.ipa" \
+ --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
@@ -500,8 +525,9 @@ jobs:
cat apps.json
- name: Commit and push
+ env:
+ VERSION: ${{ needs.get-version.outputs.version }}
run: |
- VERSION="${{ needs.get-version.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add apps.json
@@ -515,24 +541,24 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
fetch-depth: 0
- name: Download Android APK
- uses: actions/download-artifact@v7
+ uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: android-apk
path: ./release
- name: Download iOS IPA
- uses: actions/download-artifact@v7
+ 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@v4
+ uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4
with:
config: cliff.toml
args: --latest --strip all
@@ -574,8 +600,9 @@ jobs:
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: |
- VERSION=${{ needs.get-version.outputs.version }}
CHANGELOG=$(cat /tmp/changelog.txt)
# Find APK files
@@ -589,7 +616,7 @@ jobs:
"What's New:" \
"${CHANGELOG}" \
"" \
- "View Release Notes" \
+ "View Release Notes" \
> /tmp/telegram_message.txt
MESSAGE=$(cat /tmp/telegram_message.txt)