Skip to content

Improve mobile/desktop UX and tab bar visuals; bump version to 1.0.10 #12

Improve mobile/desktop UX and tab bar visuals; bump version to 1.0.10

Improve mobile/desktop UX and tab bar visuals; bump version to 1.0.10 #12

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
permissions:
contents: write
jobs:
build-android:
runs-on: ubuntu-latest
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
- name: Install dependencies
run: flutter pub get
- name: Configure Android signing
if: ${{ env.ANDROID_KEYSTORE_BASE64 != '' }}
shell: bash
run: |
python3 - <<'PY'
import base64
import os
import re
import sys
raw = os.environ.get("ANDROID_KEYSTORE_BASE64", "")
if raw.startswith("ANDROID_KEYSTORE_BASE64="):
raw = raw.split("=", 1)[1]
raw = raw.strip().strip('"').strip("'")
clean = re.sub(r"[^A-Za-z0-9+/=]", "", raw)
if not clean:
sys.exit("ANDROID_KEYSTORE_BASE64 is empty after normalization.")
clean += "=" * ((4 - len(clean) % 4) % 4)
try:
decoded = base64.b64decode(clean, validate=False)
except Exception as exc:
sys.exit(f"Failed to decode ANDROID_KEYSTORE_BASE64: {exc}")
with open("android/release.keystore", "wb") as f:
f.write(decoded)
PY
STORE_PASSWORD="$(printf '%s' "${ANDROID_STORE_PASSWORD}" | tr -d '\r\n')"
KEY_ALIAS="$(printf '%s' "${ANDROID_KEY_ALIAS}" | tr -d '\r\n')"
KEY_PASSWORD="$(printf '%s' "${ANDROID_KEY_PASSWORD}" | tr -d '\r\n')"
cat > android/key.properties <<EOF
storeFile=../release.keystore
storePassword=${STORE_PASSWORD}
keyAlias=${KEY_ALIAS}
keyPassword=${KEY_PASSWORD}
EOF
- name: Validate Android signing credentials
if: ${{ env.ANDROID_KEYSTORE_BASE64 != '' }}
shell: bash
run: |
STORE_PASSWORD="$(printf '%s' "${ANDROID_STORE_PASSWORD}" | tr -d '\r\n')"
KEY_ALIAS="$(printf '%s' "${ANDROID_KEY_ALIAS}" | tr -d '\r\n')"
KEY_PASSWORD="$(printf '%s' "${ANDROID_KEY_PASSWORD}" | tr -d '\r\n')"
set +e
keytool -list -keystore android/release.keystore -storepass "${STORE_PASSWORD}" >/tmp/keystore-list.txt 2>/tmp/keystore-list.err
LIST_STATUS=$?
if [ ${LIST_STATUS} -ne 0 ]; then
echo "Keystore validation failed: store password is incorrect or keystore content is invalid."
cat /tmp/keystore-list.err
exit 1
fi
keytool -list -keystore android/release.keystore -storepass "${STORE_PASSWORD}" -alias "${KEY_ALIAS}" >/tmp/alias-check.txt 2>/tmp/alias-check.err
ALIAS_STATUS=$?
if [ ${ALIAS_STATUS} -ne 0 ]; then
echo "Alias validation failed: ANDROID_KEY_ALIAS does not exist in keystore."
echo "Available aliases:"
cat /tmp/keystore-list.txt | sed -n '1,80p'
cat /tmp/alias-check.err
exit 1
fi
keytool -importkeystore \
-srckeystore android/release.keystore \
-srcstorepass "${STORE_PASSWORD}" \
-srcalias "${KEY_ALIAS}" \
-srckeypass "${KEY_PASSWORD}" \
-destkeystore /tmp/verify-signing.jks \
-deststorepass "tempVerify123" \
-destkeypass "tempVerify123" \
-noprompt >/tmp/keypass-check.txt 2>/tmp/keypass-check.err
KEYPASS_STATUS=$?
if [ ${KEYPASS_STATUS} -ne 0 ]; then
echo "Key password validation failed: ANDROID_KEY_PASSWORD does not match the key for alias '${KEY_ALIAS}'."
cat /tmp/keypass-check.err
exit 1
fi
set -e
- name: Build Android release APK
if: ${{ env.ANDROID_KEYSTORE_BASE64 != '' }}
run: flutter build apk --release
- name: Build Android debug APK (fallback)
if: ${{ env.ANDROID_KEYSTORE_BASE64 == '' }}
run: flutter build apk --debug
- name: Prepare Android artifact
shell: bash
run: |
TAG_NAME="${GITHUB_REF_NAME:-manual}"
mkdir -p dist
if [ -f build/app/outputs/flutter-apk/app-release.apk ]; then
cp build/app/outputs/flutter-apk/app-release.apk "dist/NeverMissAlarm-android-${TAG_NAME}.apk"
else
cp build/app/outputs/flutter-apk/app-debug.apk "dist/NeverMissAlarm-android-${TAG_NAME}.apk"
fi
- name: Upload Android artifact
uses: actions/upload-artifact@v4
with:
name: android-apk
path: dist/*.apk
build-windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
- name: Install dependencies
run: flutter pub get
- name: Build Windows release
run: flutter build windows --release
- name: Install Inno Setup
shell: pwsh
run: choco install innosetup --no-progress -y
- name: Build Windows installer
shell: pwsh
run: |
$tag = "${env:GITHUB_REF_NAME}"
if ([string]::IsNullOrWhiteSpace($tag)) { $tag = "v0.0.0" }
$version = $tag.TrimStart("v")
$iscc = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
& $iscc "/DMyAppVersion=$version" "windows\\installer\\NeverMissAlarm.iss"
- name: Create Windows zip artifact
shell: pwsh
run: |
$tag = "${env:GITHUB_REF_NAME}"
if ([string]::IsNullOrWhiteSpace($tag)) { $tag = "manual" }
New-Item -ItemType Directory -Path dist -Force | Out-Null
$zipPath = "dist/NeverMissAlarm-windows-$tag.zip"
Compress-Archive -Path "build/windows/x64/runner/Release/*" -DestinationPath $zipPath -Force
$version = $tag.TrimStart("v")
$installer = "build/windows/installer/NeverMissAlarmSetup_$version.exe"
if (Test-Path $installer) {
Copy-Item $installer "dist/NeverMissAlarm-windows-$tag-setup.exe" -Force
} else {
throw "Installer not found: $installer"
}
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: windows-artifacts
path: |
dist/*.zip
dist/*.exe
publish-release:
needs:
- build-android
- build-windows
runs-on: ubuntu-latest
steps:
- name: Download Android artifact
uses: actions/download-artifact@v4
with:
name: android-apk
path: dist
- name: Download Windows artifact
uses: actions/download-artifact@v4
with:
name: windows-artifacts
path: dist
- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
files: |
dist/*.apk
dist/*.zip
dist/*.exe
draft: false
prerelease: false
generate_release_notes: true