Skip to content

Merge pull request #4 from tqmane/copilot/fix-ai-image-test-functiona… #201

Merge pull request #4 from tqmane/copilot/fix-ai-image-test-functiona…

Merge pull request #4 from tqmane/copilot/fix-ai-image-test-functiona… #201

Workflow file for this run

name: Flutter Build & Release
on:
push:
branches: [ main, master ]
tags:
- 'v*'
pull_request:
branches: [ main, master ]
workflow_dispatch:
jobs:
build-linux:
runs-on: ubuntu-latest
env:
DESKTOP_GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.DESKTOP_GOOGLE_OAUTH_CLIENT_ID }}
DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.3'
channel: 'stable'
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev
- name: Flutter doctor
run: flutter doctor -v
- name: Create Linux platform structure
working-directory: flutter-app
run: |
# 既存のlinuxフォルダを削除して再生成(アイコンは保持)
if [ -f "linux/app_icon.png" ]; then
cp linux/app_icon.png /tmp/app_icon_linux.png
fi
rm -rf linux
flutter create --project-name similarity_quiz --org com.tqmane --platforms=linux .
if [ -f "/tmp/app_icon_linux.png" ]; then
cp /tmp/app_icon_linux.png linux/app_icon.png
fi
- name: Remove Firebase dependencies for desktop
working-directory: flutter-app
run: |
# Firebaseはデスクトップでサポートされていないため削除
sed -i '/firebase_core:/d' pubspec.yaml
sed -i '/firebase_auth:/d' pubspec.yaml
sed -i '/firebase_database:/d' pubspec.yaml
sed -i '/google_sign_in:/d' pubspec.yaml
# Firebase初期化ファイルをデスクトップ用スタブに置き換え
cp lib/services/firebase_init_desktop.dart lib/services/firebase_init_io.dart
# Firebase同期サービス(デスクトップREST版)に置き換え
cp lib/services/firebase_sync_service_desktop.dart lib/services/firebase_sync_service_platform.dart
# 管理者画面をスタブに置き換え
cp lib/screens/admin_screen_stub.dart lib/screens/admin_screen_mobile.dart
- name: Set Linux app icon
working-directory: flutter-app
run: |
# デスクトップファイル用にアイコンをコピー
if [ -f "assets/icon/app_icon.png" ] && [ -d "linux" ]; then
cp assets/icon/app_icon.png linux/
# my_application.ccでウィンドウアイコンを設定
if [ -f "linux/my_application.cc" ]; then
python3 -c "
import os
file_path = 'linux/my_application.cc'
if os.path.exists(file_path):
with open(file_path, 'r') as f:
content = f.read()
target_str = 'gtk_window_set_title(window, "similarity_quiz");'
replacement = 'gtk_window_set_title(window, "判別クイズ");'
# アイコン設定コード
icon_code = '''
# アプリアイコンを設定
GError *error = NULL;
gchar *icon_path = g_build_filename(g_get_current_dir(), "data", "flutter_assets", "assets", "icon", "app_icon.png", NULL);
if (g_file_test(icon_path, G_FILE_TEST_EXISTS)) {
gtk_window_set_icon_from_file(window, icon_path, &error);
if (error != NULL) {
g_warning("Failed to set icon: %s", error->message);
g_error_free(error);
}
}
g_free(icon_path);'''
if target_str in content:
new_content = content.replace(target_str, replacement + '\n' + icon_code)
with open(file_path, 'w') as f:
f.write(new_content)
print('Updated my_application.cc')
else:
print('Target string not found in my_application.cc')
"
fi
fi
- name: Clean and get dependencies
working-directory: flutter-app
run: |
flutter clean
flutter pub get
- name: Run tests
working-directory: flutter-app
run: flutter test || echo "No tests found"
- name: Build Linux
working-directory: flutter-app
run: flutter build linux --release --dart-define=DESKTOP_GOOGLE_OAUTH_CLIENT_ID=$DESKTOP_GOOGLE_OAUTH_CLIENT_ID --dart-define=DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET=$DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET
- name: Package Linux build
run: |
cd flutter-app/build/linux/x64/release/bundle
tar -czvf ../../../../../similarity_quiz-linux-x64.tar.gz .
- name: Upload Linux build
uses: actions/upload-artifact@v4
with:
name: linux-build
path: flutter-app/similarity_quiz-linux-x64.tar.gz
build-windows:
runs-on: windows-latest
env:
DESKTOP_GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.DESKTOP_GOOGLE_OAUTH_CLIENT_ID }}
DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.3'
channel: 'stable'
- name: Flutter doctor
run: flutter doctor -v
- name: Create Windows platform structure
working-directory: flutter-app
run: |
# 既存のwindowsフォルダを削除して再生成(アイコンは保持)
if (Test-Path "windows/runner/resources") {
Copy-Item "windows/runner/resources/*.png" "$env:TEMP\" -ErrorAction SilentlyContinue
}
Remove-Item -Recurse -Force "windows" -ErrorAction SilentlyContinue
flutter create --project-name similarity_quiz --org com.tqmane --platforms=windows .
if (Test-Path "$env:TEMP\app_icon*.png") {
Copy-Item "$env:TEMP\app_icon*.png" "windows/runner/resources\" -ErrorAction SilentlyContinue
}
- name: Remove Firebase dependencies for desktop
working-directory: flutter-app
shell: pwsh
run: |
# Firebaseはデスクトップでサポートされていないため削除
$content = Get-Content pubspec.yaml -Raw
$content = $content -replace ' firebase_core:.*\n', ''
$content = $content -replace ' firebase_auth:.*\n', ''
$content = $content -replace ' firebase_database:.*\n', ''
$content = $content -replace ' google_sign_in:.*\n', ''
Set-Content pubspec.yaml $content
# Firebase初期化ファイルをデスクトップ用スタブに置き換え
Copy-Item lib/services/firebase_init_desktop.dart lib/services/firebase_init_io.dart -Force
# Firebase同期サービス(デスクトップREST版)に置き換え
Copy-Item lib/services/firebase_sync_service_desktop.dart lib/services/firebase_sync_service_platform.dart -Force
# 管理者画面をスタブに置き換え
Copy-Item lib/screens/admin_screen_stub.dart lib/screens/admin_screen_mobile.dart -Force
- name: Set Windows app icon
working-directory: flutter-app
shell: pwsh
run: |
# Windows用のアイコンを設定
if ((Test-Path "assets/icon/app_icon.png") -and (Test-Path "windows/runner/resources")) {
# PNGからICOに変換(magickがインストールされていれば)
# windows/runner/resources/app_icon.ico として保存
Copy-Item "assets/icon/app_icon.png" "windows/runner/resources/app_icon.png"
# Runner.rcのアイコン参照を更新
$rcFile = "windows/runner/Runner.rc"
if (Test-Path $rcFile) {
(Get-Content $rcFile) -replace 'IDI_APP_ICON\s+ICON\s+"resources\\app_icon.ico"', 'IDI_APP_ICON ICON "resources\app_icon.ico"' | Set-Content $rcFile
}
}
- name: Clean and get dependencies
working-directory: flutter-app
run: |
flutter clean
flutter pub get
- name: Build Windows
working-directory: flutter-app
run: flutter build windows --release --dart-define=DESKTOP_GOOGLE_OAUTH_CLIENT_ID=$env:DESKTOP_GOOGLE_OAUTH_CLIENT_ID --dart-define=DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET=$env:DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET
- name: Package Windows build
run: |
Compress-Archive -Path flutter-app/build/windows/x64/runner/Release/* -DestinationPath flutter-app/similarity_quiz-windows-x64.zip
- name: Upload Windows build
uses: actions/upload-artifact@v4
with:
name: windows-build
path: flutter-app/similarity_quiz-windows-x64.zip
build-macos:
runs-on: macos-latest
env:
DESKTOP_GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.DESKTOP_GOOGLE_OAUTH_CLIENT_ID }}
DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.3'
channel: 'stable'
- name: Flutter doctor
run: flutter doctor -v
- name: Create macOS platform structure
working-directory: flutter-app
run: |
# 既存のmacosフォルダを削除して再生成(アイコンは保持)
if [ -d "macos/Runner/Assets.xcassets/AppIcon.appiconset" ]; then
cp -r macos/Runner/Assets.xcassets/AppIcon.appiconset /tmp/macos_icons
fi
rm -rf macos
flutter create --project-name similarity_quiz --org com.tqmane --platforms=macos .
if [ -d "/tmp/macos_icons" ]; then
cp -r /tmp/macos_icons/* macos/Runner/Assets.xcassets/AppIcon.appiconset/
fi
- name: Remove Firebase dependencies for desktop
working-directory: flutter-app
run: |
# Firebaseはデスクトップでサポートされていないため削除
sed -i '' '/firebase_core:/d' pubspec.yaml
sed -i '' '/firebase_auth:/d' pubspec.yaml
sed -i '' '/firebase_database:/d' pubspec.yaml
sed -i '' '/google_sign_in:/d' pubspec.yaml
# Firebase初期化ファイルをデスクトップ用スタブに置き換え
cp lib/services/firebase_init_desktop.dart lib/services/firebase_init_io.dart
# Firebase同期サービス(デスクトップREST版)に置き換え
cp lib/services/firebase_sync_service_desktop.dart lib/services/firebase_sync_service_platform.dart
# 管理者画面をスタブに置き換え
cp lib/screens/admin_screen_stub.dart lib/screens/admin_screen_mobile.dart
- name: Clean and get dependencies
working-directory: flutter-app
run: |
flutter clean
flutter pub get
- name: Build macOS
working-directory: flutter-app
run: flutter build macos --release --dart-define=DESKTOP_GOOGLE_OAUTH_CLIENT_ID=$DESKTOP_GOOGLE_OAUTH_CLIENT_ID --dart-define=DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET=$DESKTOP_GOOGLE_OAUTH_CLIENT_SECRET
- name: Package macOS build
run: |
cd flutter-app/build/macos/Build/Products/Release
zip -r ../../../../../similarity_quiz-macos.zip "similarity_quiz.app"
- name: Upload macOS build
uses: actions/upload-artifact@v4
with:
name: macos-build
path: flutter-app/similarity_quiz-macos.zip
build-ios:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.3'
channel: 'stable'
- name: Flutter doctor
run: flutter doctor -v
- name: Create iOS platform structure
working-directory: flutter-app
run: |
# 既存のiosフォルダの設定を保持
if [ -d "ios/Runner/Assets.xcassets/AppIcon.appiconset" ]; then
cp -r ios/Runner/Assets.xcassets/AppIcon.appiconset /tmp/ios_icons
fi
# GoogleService-Info.plistをバックアップ
if [ -f "ios/Runner/GoogleService-Info.plist" ]; then
cp ios/Runner/GoogleService-Info.plist /tmp/GoogleService-Info.plist
fi
# Info.plistをバックアップ(Google Sign-In設定を保持)
if [ -f "ios/Runner/Info.plist" ]; then
cp ios/Runner/Info.plist /tmp/Info.plist
fi
rm -rf ios
flutter create --project-name similarity_quiz --org com.tqmane --platforms=ios .
if [ -d "/tmp/ios_icons" ]; then
cp -r /tmp/ios_icons/* ios/Runner/Assets.xcassets/AppIcon.appiconset/
fi
# GoogleService-Info.plistを復元
if [ -f "/tmp/GoogleService-Info.plist" ]; then
cp /tmp/GoogleService-Info.plist ios/Runner/GoogleService-Info.plist
fi
# Info.plistを復元(GIDClientID, GIDServerClientID, CFBundleURLTypesを保持)
if [ -f "/tmp/Info.plist" ]; then
cp /tmp/Info.plist ios/Runner/Info.plist
fi
- name: Setup CocoaPods
run: |
gem install cocoapods
pod --version
- name: Set iOS deployment target to 16.4
working-directory: flutter-app/ios
run: |
# Podfileの最小バージョンを16.4に設定
sed -i '' "s/platform :ios, '.*'/platform :ios, '16.4'/" Podfile
# project.pbxprojのIPHONEOS_DEPLOYMENT_TARGETを更新
sed -i '' 's/IPHONEOS_DEPLOYMENT_TARGET = [0-9.]*;/IPHONEOS_DEPLOYMENT_TARGET = 16.4;/g' Runner.xcodeproj/project.pbxproj
- name: Clean and get dependencies
working-directory: flutter-app
run: |
flutter clean
flutter pub get
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: flutter-app/ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('flutter-app/ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Install CocoaPods dependencies
working-directory: flutter-app/ios
run: |
pod repo update
pod install --verbose
- name: Build iOS (no codesign)
working-directory: flutter-app
run: flutter build ios --release --no-codesign
- name: Create IPA package
run: |
cd flutter-app/build/ios/iphoneos
mkdir -p Payload
cp -r Runner.app Payload/
zip -r ../../../similarity_quiz-ios.ipa Payload
- name: Upload iOS build
uses: actions/upload-artifact@v4
with:
name: ios-build
path: flutter-app/similarity_quiz-ios.ipa
build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.38.3'
channel: 'stable'
- name: Flutter doctor
run: flutter doctor -v
- name: Create Android platform structure
working-directory: flutter-app
run: |
# 既存のandroidフォルダを削除して再生成(アイコンとgoogle-services.jsonは保持)
if [ -d "android/app/src/main/res" ]; then
cp -r android/app/src/main/res /tmp/android_res
fi
if [ -f "android/app/google-services.json" ]; then
cp android/app/google-services.json /tmp/google-services.json
fi
rm -rf android
flutter create --project-name similarity_quiz --org com.tqmane --platforms=android .
if [ -d "/tmp/android_res" ]; then
cp -r /tmp/android_res/* android/app/src/main/res/
fi
if [ -f "/tmp/google-services.json" ]; then
cp /tmp/google-services.json android/app/google-services.json
fi
- name: Disable Impeller in AndroidManifest
working-directory: flutter-app
run: |
# AndroidManifest.xmlにImpeller無効化フラグを追加
python3 -c "
with open('android/app/src/main/AndroidManifest.xml', 'r') as f:
content = f.read()
impeller_meta = '''
<meta-data
android:name=\"io.flutter.embedding.android.EnableImpeller\"
android:value=\"false\" />'''
if 'EnableImpeller' not in content:
content = content.replace('</application>', impeller_meta + '\n </application>')
with open('android/app/src/main/AndroidManifest.xml', 'w') as f:
f.write(content)
"
- name: Configure Android build for desugaring
working-directory: flutter-app
run: |
# Kotlin DSL (.gradle.kts) または Groovy DSL (.gradle) の両方に対応
BUILD_FILE=""
if [ -f "android/app/build.gradle.kts" ]; then
BUILD_FILE="android/app/build.gradle.kts"
echo "Using Kotlin DSL: $BUILD_FILE"
elif [ -f "android/app/build.gradle" ]; then
BUILD_FILE="android/app/build.gradle"
echo "Using Groovy DSL: $BUILD_FILE"
else
echo "ERROR: No build.gradle found!"
ls -la android/app/
exit 1
fi
# Kotlin DSL用の設定
if [[ "$BUILD_FILE" == *.kts ]]; then
python3 -c "
with open('$BUILD_FILE', 'r') as f:
content = f.read()
# isCoreLibraryDesugaringEnabledを追加
if 'isCoreLibraryDesugaringEnabled' not in content:
content = content.replace(
'compileOptions {',
'compileOptions {\n isCoreLibraryDesugaringEnabled = true'
)
# multiDexEnabledを追加
if 'multiDexEnabled' not in content:
content = content.replace(
'defaultConfig {',
'defaultConfig {\n multiDexEnabled = true'
)
# dependenciesにdesugar_jdk_libsを追加
if 'desugar_jdk_libs' not in content:
if 'dependencies {' in content:
content = content.replace(
'dependencies {',
'dependencies {\n coreLibraryDesugaring(\"com.android.tools:desugar_jdk_libs:2.0.4\")'
)
else:
content += '''
dependencies {
coreLibraryDesugaring(\"com.android.tools:desugar_jdk_libs:2.0.4\")
}
'''
with open('$BUILD_FILE', 'w') as f:
f.write(content)
print('build.gradle.kts updated successfully')
"
else
# Groovy DSL用の設定
python3 -c "
with open('$BUILD_FILE', 'r') as f:
content = f.read()
# compileOptionsにcoreLibraryDesugaringEnabledを追加
if 'coreLibraryDesugaringEnabled' not in content:
content = content.replace(
'compileOptions {',
'compileOptions {\n coreLibraryDesugaringEnabled true'
)
# Java 17に更新
content = content.replace('JavaVersion.VERSION_1_8', 'JavaVersion.VERSION_17')
content = content.replace("jvmTarget = '1.8'", "jvmTarget = '17'")
# multiDexEnabledを追加
if 'multiDexEnabled' not in content:
content = content.replace(
'versionName flutterVersionName',
'versionName flutterVersionName\n multiDexEnabled true'
)
# dependenciesにdesugar_jdk_libsを追加
if 'desugar_jdk_libs' not in content:
if 'dependencies {' in content:
content = content.replace(
'dependencies {',
\"dependencies {\\n coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'\"
)
else:
content += \"\\n\\ndependencies {\\n coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'\\n}\\n\"
with open('$BUILD_FILE', 'w') as f:
f.write(content)
print('build.gradle updated successfully')
"
fi
# settings.gradle(.kts)のKotlinバージョンを更新
if [ -f "android/settings.gradle.kts" ]; then
sed -i 's/kotlin(\"android\") version \"[0-9.]*\"/kotlin(\"android\") version \"1.9.22\"/' android/settings.gradle.kts
echo "settings.gradle.kts updated"
elif [ -f "android/settings.gradle" ]; then
sed -i 's/org.jetbrains.kotlin.android\" version \"[0-9.]*\"/org.jetbrains.kotlin.android\" version \"1.9.22\"/' android/settings.gradle
echo "settings.gradle updated"
fi
- name: Get dependencies
working-directory: flutter-app
run: flutter pub get
- name: Build APK
working-directory: flutter-app
run: flutter build apk --release
- name: Sign APK with apksigner (v4)
if: env.KEYSTORE_BASE64 != ''
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: |
UNSIGNED_APK="flutter-app/build/app/outputs/flutter-apk/app-release.apk"
SIGNED_APK="flutter-app/build/app/outputs/flutter-apk/similarity_quiz-signed.apk"
# キーストアをデコード
echo "$KEYSTORE_BASE64" | base64 -d > release.keystore
# zipalignで最適化
${ANDROID_HOME}/build-tools/34.0.0/zipalign -v -p 4 "$UNSIGNED_APK" "${UNSIGNED_APK%.apk}-aligned.apk"
# apksignerで署名
${ANDROID_HOME}/build-tools/34.0.0/apksigner sign \
--ks release.keystore \
--ks-key-alias "$KEY_ALIAS" \
--ks-pass pass:"$KEYSTORE_PASSWORD" \
--key-pass pass:"$KEY_PASSWORD" \
--v4-signing-enabled true \
--out "$SIGNED_APK" \
"${UNSIGNED_APK%.apk}-aligned.apk"
# 署名の検証
${ANDROID_HOME}/build-tools/34.0.0/apksigner verify --verbose "$SIGNED_APK"
# 一時ファイルをクリーンアップ
rm -f release.keystore "${UNSIGNED_APK%.apk}-aligned.apk"
- name: Rename unsigned APK (fallback)
if: env.KEYSTORE_BASE64 == ''
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
mv flutter-app/build/app/outputs/flutter-apk/app-release.apk flutter-app/build/app/outputs/flutter-apk/similarity_quiz-unsigned.apk
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: flutter-app/build/app/outputs/flutter-apk/similarity_quiz-*.apk
# リリースジョブ
release:
runs-on: ubuntu-latest
needs: [build-linux, build-windows, build-macos, build-ios, build-android]
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Display structure
run: ls -R artifacts
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
artifacts/linux-build/*.tar.gz
artifacts/windows-build/*.zip
artifacts/macos-build/*.zip
artifacts/ios-build/*.ipa
artifacts/android-apk/*.apk
generate_release_notes: true
name: "Flutter App ${{ github.ref_name }}"
body: |
## Flutter 判別クイズアプリ
### ダウンロード
- **Windows**: similarity_quiz-windows-x64.zip
- **macOS**: similarity_quiz-macos.zip
- **Linux**: similarity_quiz-linux-x64.tar.gz
- **iOS**: similarity_quiz-ios.ipa (要署名/AltStore等)
- **Android (Quiz App)**: similarity_quiz-unsigned.apk / signed.apk
### インストール方法
- Windows: ZIPを解凍して `similarity_quiz.exe` を実行
- macOS: ZIPを解凍して `similarity_quiz.app` を実行
- Linux: `tar -xzvf` で解凍して `similarity_quiz` を実行
- iOS: AltStore/Sideloadly等でインストール
- Android: APKをインストール
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}