fix(ci): harden release workflow inputs and actions

This commit is contained in:
zarzet
2026-08-29 18:57:22 +07:00
parent 97fdc85513
commit 760d796673
4 changed files with 116 additions and 73 deletions
+28 -12
View File
@@ -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"
+12 -12
View File
@@ -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"
+4 -4
View File
@@ -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
+72 -45
View File
@@ -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:
"<b>What's New:</b>" \
"${CHANGELOG}" \
"" \
"<a href=\"https://github.com/${{ github.repository }}/releases/tag/${VERSION}\">View Release Notes</a>" \
"<a href=\"https://github.com/${REPOSITORY}/releases/tag/${VERSION}\">View Release Notes</a>" \
> /tmp/telegram_message.txt
MESSAGE=$(cat /tmp/telegram_message.txt)