diff --git a/.github/licenses_checker.yaml b/.github/licenses_checker.yaml new file mode 100644 index 0000000..3b2abb1 --- /dev/null +++ b/.github/licenses_checker.yaml @@ -0,0 +1,18 @@ +permittedLicenses: + - MIT + - BSD-3-Clause + - MIT-Modern-Variant + - Zlib + - Apache-2.0 + - BSD-2-Clause + - ISC + - MPL-2.0 + +approvedPackages: + no-file: + - flutter_localizations + - flutter_test + - flutter_web_plugins + +rejectedLicenses: + - GPL diff --git a/.github/workflows/deploy_web.yml b/.github/workflows/deploy_web.yml deleted file mode 100644 index 5767ac7..0000000 --- a/.github/workflows/deploy_web.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Deploy Flutter Web to GitHub Pages -on: - push: - branches: - - main - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - actions: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: Setup Flutter - uses: subosito/flutter-action@v2 - - - name: Get dependencies - run: flutter pub get - - - name: Build web - run: flutter build web --release --base-href /${{ github.event.repository.name }}/ - - - name: Setup Pages - uses: actions/configure-pages@v4 - with: - enablement: true - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: "./build/web" - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/flutter_workflow.yml b/.github/workflows/flutter_workflow.yml index c209a3b..02b56d8 100644 --- a/.github/workflows/flutter_workflow.yml +++ b/.github/workflows/flutter_workflow.yml @@ -1,42 +1,43 @@ name: Flutter CI on: - push: - branches: - - main workflow_dispatch: - pull_request: - branches: - - main + # pull_request: + # branches: + # - main jobs: flutter_ci: name: Run flutter test and analyze runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - name: Checkout repositoy + uses: actions/checkout@v5 + + - name: Install flutter + uses: subosito/flutter-action@v2 + + - name: Prepare repository + uses: Spaccesi/flutter-actions-suite/prepare@main with: - distribution: "zulu" - java-version: "12" - - uses: subosito/flutter-action@v2 + run-build-runner: "false" + run-gen-l10n: "true" + gen-l10n-fails-if-untranslated: "true" + + - name: Check repository + uses: Spaccesi/flutter-actions-suite/check@main with: - channel: "stable" - - run: flutter pub get - - run: flutter analyze - - run: flutter test - - name: Generate localizations - run: flutter gen-l10n - - name: Check for untranslated messages + run-license: "true" + compatible-licenses-conf-path: ".github/licenses_checker.yaml" + + - name: Check license headers + continue-on-error: true run: | - content=$(cat untranslated_messages.json) - if [ "$content" = "{}" ]; then - echo "✅ All translations are complete!" - exit 0 - else - echo "❌ Found untranslated messages:" - cat untranslated_messages.json - echo "" - echo "Please add the missing translations to the corresponding .arb files" + MISSING_HEADERS=$(find lib test -name "*.dart" ! -name "*.g.dart" ! -name "*.freezed.dart" ! -name "*.mocks.dart" ! -name "app_localizations*.dart" -exec sh -c '! grep -q "^// Copyright (C) 2026 Víctor Carreras" "$1" && echo "$1"' _ {} \;) + if [ -n "$MISSING_HEADERS" ]; then + echo "❌ The following files are missing the GPL-3.0 license header:" + echo "$MISSING_HEADERS" exit 1 + else + echo "✅ All files have the correct GPL-3.0 license header." fi diff --git a/.github/workflows/prod_deloy.yml b/.github/workflows/prod_deloy.yml new file mode 100644 index 0000000..c64ccbf --- /dev/null +++ b/.github/workflows/prod_deloy.yml @@ -0,0 +1,233 @@ +name: Flutter PROD deployment +on: + push: + # tags: + # - "v[0-9]+.[0-9]+.[0-9]+*" + branches: + - feature/github-actions-refactor + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + actions: write + +jobs: + prepare_and_check: + name: Prepare and check repository + runs-on: ubuntu-latest + steps: + - name: Checkout repositoy + uses: actions/checkout@v5 + + - name: Install flutter + uses: subosito/flutter-action@v2 + + - name: Prepare repository + uses: Spaccesi/flutter-ci-suite/prepare@main + with: + run-build-runner: "false" + run-gen-l10n: "true" + gen-l10n-fails-if-untranslated: "true" + + - name: Check repository + uses: Spaccesi/flutter-ci-suite/check@main + with: + run-license: "true" + compatible-licenses-conf-path: ".github/licenses_checker.yaml" + + - name: Check license headers + continue-on-error: true + run: | + MISSING_HEADERS=$(find lib test -name "*.dart" ! -name "*.g.dart" ! -name "*.freezed.dart" ! -name "*.mocks.dart" ! -name "app_localizations*.dart" -exec sh -c '! grep -q "^// Copyright (C) 2026 Víctor Carreras" "$1" && echo "$1"' _ {} \;) + if [ -n "$MISSING_HEADERS" ]; then + echo "❌ The following files are missing the GPL-3.0 license header:" + echo "$MISSING_HEADERS" + exit 1 + else + echo "✅ All files have the correct GPL-3.0 license header." + fi + + - name: Upload prepared workspace + uses: actions/upload-artifact@v4 + with: + name: prepared-repo + path: | + . + !.git + retention-days: 1 + + build_and_release_web: + name: Build and release web on GitHub Pages + runs-on: ubuntu-latest + needs: [prepare_and_check] + steps: + - name: Download prepared workspace + uses: actions/download-artifact@v4 + with: + name: prepared-repo + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + + - name: Build & Publish web + uses: Spaccesi/flutter-ci-suite@main + with: + base-href: "/${{ github.event.repository.name }}/" + run-test: "false" + run-build-runner: "false" + run-gen-l10n: "false" + run-analyze: "false" + run-license: "false" + build-platform: "web" + publish-destination: "github-pages" + + build_and_release_ios: + name: Build and release ios on App Store + runs-on: macos-latest + needs: [prepare_and_check] + steps: + - name: Download prepared workspace + uses: actions/download-artifact@v4 + with: + name: prepared-repo + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + + - name: Build ios + uses: Spaccesi/flutter-ci-suite/build/ios@main + with: + ios-distribution-certificate-base64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }} + ios-distribution-certificate-password: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_PASSWORD }} + ios-provisioning-profile-base64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} + + - name: Publish ios + uses: Spaccesi/flutter-ci-suite/publish/ios-app-store@main + with: + apple-api-key-id: ${{ secrets.APPLE_API_KEY_ID }} + apple-api-key-issuer-id: ${{ secrets.APPLE_API_KEY_ISSUER }} + apple-api-key-content: ${{ secrets.APPLE_API_KEY_CONTENT }} + + build_and_release_macos: + name: Build and release macos on App Store + runs-on: macos-latest + needs: [prepare_and_check] + steps: + - name: Download prepared workspace + uses: actions/download-artifact@v4 + with: + name: prepared-repo + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + + - name: Build macos + uses: Spaccesi/flutter-ci-suite/build/macos@main + with: + macos-installer-certificate-base64: ${{ secrets.MACOS_INSTALLER_CERTIFICATE_BASE64 }} + macos-installer-certificate-password: ${{ secrets.MACOS_INSTALLER_CERTIFICATE_PASSWORD }} + macos-distribution-certificate-base64: ${{ secrets.MACOS_DISTRIBUTION_CERTIFICATE_BASE64 }} + macos-distribution-certificate-password: ${{ secrets.MACOS_DISTISTRIBUTION_CERTIFICATE_PASSWORD }} + macos-provisioning-profile-base64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} + + - name: Publish macos + uses: Spaccesi/flutter-ci-suite/publish/macos-app-store@main + with: + apple-api-key-id: ${{ secrets.APPLE_API_KEY_ID }} + apple-api-key-issuer-id: ${{ secrets.APPLE_API_KEY_ISSUER }} + apple-api-key-content: ${{ secrets.APPLE_API_KEY_CONTENT }} + + build_and_release_linux: + name: Build and release linux on Snap Store + runs-on: ubuntu-latest + needs: [prepare_and_check] + steps: + - name: Download prepared workspace + uses: actions/download-artifact@v4 + with: + name: prepared-repo + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + + - name: Build linux + uses: Spaccesi/flutter-ci-suite/build/linux@main + + - name: Publish on snap + uses: Spaccesi/flutter-ci-suite/publish/snap-store@main + with: + snap-store-token: ${{ secrets.SNAPCRAFT_CREDENTIALS }} + + build_and_release_android: + name: Build and release android on Google Play + runs-on: ubuntu-latest + needs: [prepare_and_check] + steps: + - name: Download prepared workspace + uses: actions/download-artifact@v4 + with: + name: prepared-repo + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + + - name: Build android + uses: Spaccesi/flutter-ci-suite/build/android@main + with: + android-store-file-base64: ${{ secrets.ANDROID_STORE_FILE_BASSE64 }} + android-key-password: ${{ secrets.ANDROID_KEY_PASSWORD }} + android-store-password: ${{ secrets.ANDROID_STORE_PASSWORD }} + android-key-alias: ${{ secrets.ANDROID_KEY_ALIAS }} + + - name: Publish Android + uses: Spaccesi/flutter-ci-suite/publish/play-store@main + with: + android-service-account-json: ${{ secrets.ANDROID_SERVICE_ACCOUNT_JSON }} + android-package-name: es.victorcarreras.oraffle + android-track: production + android-release-files: build/app/outputs/bundle/release/app-release.aab + + build_and_release_windows: + name: Build and deploy windows on Microsoft Store + runs-on: windows-latest + needs: [prepare_and_check] + steps: + - name: Download prepared workspace + uses: actions/download-artifact@v4 + with: + name: prepared-repo + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + + - name: Build ios + uses: Spaccesi/flutter-ci-suite/build/windows@main + + - name: Publish Microsoft store + uses: Spaccesi/flutter-ci-suite/publish/microsoft-store@main + with: + microsoft-partner-center-tenant-id: ${{ secrets.MICROSOFT_PARTNER_CENTER_TENANT_ID }} + microsoft-partner-center-seller-id: ${{ secrets.MICROSOFT_PARTNER_CENTER_SELLER_ID }} + microsoft-partner-center-client-id: ${{ secrets.MICROSOFT_PARTNER_CENTER_CLIENT_ID }} + microsoft-partner-center-client-secret: ${{ secrets.MICROSOFT_PARTNER_CENTER_CLIENT_SECRET }} + + clean_up: + runs-on: ubuntu-latest + name: Clean up artifacts + needs: + [ + prepare_and_check, + build_and_release_web, + build_and_release_windows, + build_and_release_android, + build_and_release_linux, + build_and_release_macos, + build_and_release_ios, + ] + steps: + - name: Delete artifacts + uses: geekyeggo/delete-artifact@v4 + with: + name: prepared-repo diff --git a/.github/workflows/publish_android.yml b/.github/workflows/publish_android.yml deleted file mode 100644 index c8c7d53..0000000 --- a/.github/workflows/publish_android.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Publish Android Release -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+*' - workflow_dispatch: - -jobs: - publish_android_release: - name: Generate Android Release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - name: Download Android keystore - id: android_keystore - uses: timheuer/base64-to-file@v1 - with: - fileName: upload-keystore.jks - encodedString: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} - - name: Create key.properties file - run: | - echo "storeFile=${{ steps.android_keystore.outputs.filePath }}" > android/key.properties - echo "storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}" >> android/key.properties - echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/key.properties - echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/key.properties - - name: Create local.properties file - run: | - echo '${{ secrets.ANDROID_LOCAL_PROPERTIES_FILE }}' > android/local.properties - - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version: '17' - - uses: subosito/flutter-action@v2 - with: - channel: "stable" - - name: Start Android Release Build - run: flutter build appbundle - - name: Upload Release Build to Artifacts - uses: actions/upload-artifact@v4 - with: - name: android-release-artifacts - path: build/app/outputs/bundle/release/app-release.aab - - name: Release Build to internal track - uses: r0adkll/upload-google-play@v1 - with: - serviceAccountJsonPlainText: ${{ secrets.ANDROID_PLAYSTORE_ACCOUNT_KEY }} - packageName: es.victorcarreras.quiz_app - releaseFiles: build/app/outputs/bundle/release/app-release.aab - track: production - status: completed \ No newline at end of file diff --git a/.github/workflows/publish_ios.yml b/.github/workflows/publish_ios.yml deleted file mode 100644 index 5198d6d..0000000 --- a/.github/workflows/publish_ios.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Publish iOS Release - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+*' - workflow_dispatch: - -jobs: - publish_ios_release: - name: Generate iOS Release - runs-on: macos-latest - steps: - - uses: actions/checkout@v6 - - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: 'latest-stable' - - - name: Install Apple Certificate - env: - BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_DIST_CERTIFICATE_BASE64 }} - P12_PASSWORD: ${{ secrets.IOS_DIST_CERTIFICATE_PASSWORD }} - KEYCHAIN_PASSWORD: "dist_password" - run: | - # create variables - CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - # import certificate and provisioning profile from secrets - echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH - - # create temporary keychain - security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - security set-keychain-settings -lut 21600 $KEYCHAIN_PATH - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # import certificate to keychain - security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security list-keychain -d user -s $KEYCHAIN_PATH - - - name: Install Provisioning Profile - env: - PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} - run: | - # create variables - PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision - - # import provisioning profile from secrets - echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode -o $PP_PATH - - # apply provisioning profile - mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles - cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles - - - uses: subosito/flutter-action@v2 - with: - channel: 'stable' - - - name: Install dependencies - run: flutter pub get - - - name: Build IPA - run: flutter build ipa --release --export-options-plist=ios/Runner/ExportOptions.plist - - - name: Upload ipa to Artifacts - uses: actions/upload-artifact@v6 - with: - name: ios-release-artifacts - if-no-files-found: error - path: build/ios/ipa/*.ipa - - - name: Upload app to App Store Connect - env: - API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} - API_ISSUER_ID: ${{ secrets.APPLE_API_KEY_ISSUER }} - API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }} - run: | - # Create private key file - mkdir -p ~/.appstoreconnect/private_keys - echo "$API_KEY_CONTENT" > ~/.appstoreconnect/private_keys/AuthKey_$API_KEY_ID.p8 - - # Upload - xcrun altool --upload-app --type ios --file build/ios/ipa/*.ipa --apiKey "$API_KEY_ID" --apiIssuer "$API_ISSUER_ID" diff --git a/.github/workflows/publish_macos.yml b/.github/workflows/publish_macos.yml deleted file mode 100644 index 6e9c964..0000000 --- a/.github/workflows/publish_macos.yml +++ /dev/null @@ -1,150 +0,0 @@ -name: Publish macOS Release - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+*' - workflow_dispatch: - -jobs: - publish_macos_release: - name: Generate macOS Release - runs-on: macos-latest - steps: - - uses: actions/checkout@v6 - - - name: Install Apple Certificate - env: - BUILD_CERTIFICATE_BASE64: ${{ secrets.MACOS_DIST_CERTIFICATE_BASE64 }} - P12_PASSWORD: ${{ secrets.MACOS_DIST_CERTIFICATE_PASSWORD }} - KEYCHAIN_PASSWORD: "dist_password" - run: | - # create variables - CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - # import certificate and provisioning profile from secrets - echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH - - # create temporary keychain - security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - security set-keychain-settings -lut 21600 $KEYCHAIN_PATH - security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - - # import certificate to keychain - security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - security list-keychain -d user -s $KEYCHAIN_PATH - - - name: Install Installer Certificate - env: - INSTALLER_CERTIFICATE_BASE64: ${{ secrets.MACOS_INSTALLER_CERTIFICATE_BASE64 }} - P12_PASSWORD: ${{ secrets.MACOS_INSTALLER_CERTIFICATE_PASSWORD }} - KEYCHAIN_PASSWORD: "dist_password" - run: | - # create variables - CERTIFICATE_PATH=$RUNNER_TEMP/build_installer_certificate.p12 - KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - # import certificate from secrets - echo -n "$INSTALLER_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH - - echo "Checking if installer certificate exists..." - ls -l $CERTIFICATE_PATH - - # import certificate to keychain - # Using -T /usr/bin/codesign to explicitly allow usage - security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -T /usr/bin/codesign -t cert -f pkcs12 -k $KEYCHAIN_PATH - - - name: Debug Keychain Identities (Full) - run: | - echo "--- Identities (codesigning) ---" - security find-identity -v -p codesigning - echo "--- All Keychain Items ---" - # dump-keychain might be verbose but necessary here - security dump-keychain $RUNNER_TEMP/app-signing.keychain-db || true - - - name: Install Provisioning Profile - env: - PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} - run: | - # create variables - PP_PATH=$RUNNER_TEMP/build_pp.provisionprofile - - # import provisioning profile from secrets - echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode -o $PP_PATH - - # apply provisioning profile - mkdir -p ~/Library/ProvisioningProfiles - cp $PP_PATH ~/Library/ProvisioningProfiles - - # Extract UUID and copy with UUID name to ensure detection - UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i $PP_PATH)` - cp $PP_PATH ~/Library/ProvisioningProfiles/$UUID.provisionprofile - - echo "PP_PATH=$PP_PATH" >> $GITHUB_ENV - echo "profile_uuid=$UUID" >> $GITHUB_ENV - - - uses: subosito/flutter-action@v2 - with: - channel: 'stable' - - - name: Install dependencies - run: flutter pub get - - - name: Build macOS Archive - run: flutter build macos --release - - - name: Create Package with productbuild - run: | - # Find the Installer Identity Name dynamically - # Valid identities for packaging should contain "Installer" - INSTALLER_IDENTITY=$(security find-identity -v | grep "Installer" | head -1 | awk -F '"' '{print $2}') - - if [ -z "$INSTALLER_IDENTITY" ]; then - echo "No Installer identity found via find-identity, falling back to heuristic or manual name..." - # Fallback: try to find generic name in keychain dump if find-identity failed - INSTALLER_IDENTITY="3rd Party Mac Developer Installer: Victor Carreras (N2G3952MP8)" - fi - - echo "Using Installer Identity: $INSTALLER_IDENTITY" - - mkdir -p build/macos/pkg - - echo "Re-signing with Entitlements..." - # Find App Identity (Distribution) - APP_IDENTITY=$(security find-identity -v -p codesigning | grep "Apple Distribution" | head -1 | awk -F '"' '{print $2}') - if [ -z "$APP_IDENTITY" ]; then - APP_IDENTITY="Apple Distribution: Victor Carreras (N2G3952MP8)" - fi - echo "Using App Identity for signing: $APP_IDENTITY" - - echo "Embedding Provisioning Profile..." - cp "$PP_PATH" "build/macos/Build/Products/Release/Quiz Appl.app/Contents/embedded.provisionprofile" - - codesign --force --deep --options runtime --verbose --sign "$APP_IDENTITY" --entitlements macos/Runner/Release.entitlements "build/macos/Build/Products/Release/Quiz Appl.app" - - productbuild --component "build/macos/Build/Products/Release/Quiz Appl.app" /Applications \ - --sign "$INSTALLER_IDENTITY" \ - "build/macos/pkg/Quiz Appl.pkg" - - - - name: Upload pkg to Artifacts - uses: actions/upload-artifact@v4 - with: - name: macos-release-artifacts - if-no-files-found: error - path: build/macos/pkg/*.pkg - - - name: Upload app to App Store Connect - env: - API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} - API_ISSUER_ID: ${{ secrets.APPLE_API_KEY_ISSUER }} - API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }} - run: | - # Create private key file - mkdir -p ~/.appstoreconnect/private_keys - echo "$API_KEY_CONTENT" > ~/.appstoreconnect/private_keys/AuthKey_$API_KEY_ID.p8 - - # Upload - # Note: altool uses --type osx for macOS apps - xcrun altool --upload-app --type osx --file build/macos/pkg/*.pkg --apiKey "$API_KEY_ID" --apiIssuer "$API_ISSUER_ID" diff --git a/.github/workflows/publish_microsoft_store.yml b/.github/workflows/publish_microsoft_store.yml deleted file mode 100644 index 0cbbd39..0000000 --- a/.github/workflows/publish_microsoft_store.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Publish Microsoft Store -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -jobs: - publish_windows_release: - runs-on: windows-latest - steps: - - name: Checkout code - uses: actions/checkout@v5 - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable - - - uses: microsoft/setup-msstore-cli@v1.3 - - run: msstore reconfigure --tenantId ${{ secrets.PARTNER_CENTER_TENANT_ID }} --sellerId ${{ secrets.PARTNER_CENTER_SELLER_ID }} --clientId ${{ secrets.PARTNER_CENTER_CLIENT_ID }} --clientSecret ${{ secrets.PARTNER_CENTER_CLIENT_SECRET }} - - - name: Install Dart dependencies - run: flutter pub get - - - name: Create MSIX package - run: msstore package . - - - name: Publish MSIX to the Microsoft Store - run: msstore publish --appId ${{ secrets.MS_STORE_APP_ID }} -v diff --git a/.github/workflows/publish_snap.yml b/.github/workflows/publish_snap.yml deleted file mode 100644 index 06c2826..0000000 --- a/.github/workflows/publish_snap.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Publish Snap to Snapcraft -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+*' - workflow_dispatch: - -jobs: - publish_linux_release: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v5 - - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable - - - name: Install Linux build dependencies - run: sudo apt-get update && sudo apt-get install -y curl git unzip xz-utils zip libglu1-mesa clang cmake git ninja-build pkg-config liblzma-dev libstdc++-12-dev libgtk-3-dev - - - name: Install Snapcraft - run: sudo snap install snapcraft --classic - - - name: Install core24 snap - run: sudo snap install core24 --channel=latest/stable - - - name: Setup LXD (for managed builds) - uses: canonical/setup-lxd@main - - - name: Extract Flutter version - id: flutter_version - run: | - VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //;s/+.*//') - echo "VERSION=$VERSION" >> $GITHUB_ENV - - - name: Update snapcraft.yaml version - run: | - sed -i "s/^version: .*/version: \"$VERSION\"/" snap/snapcraft.yaml - - - name: Build snap - run: snapcraft pack --debug --use-lxd - - - name: Publish to Snap Store - run: snapcraft upload quiz-app*.snap --release=stable - env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_LOGIN }} diff --git a/README.md b/README.md index 38cdfb8..8dc5839 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A professional cross-platform raffle application built with Flutter. ORaffle pro Access ORaffle on your favorite platform: - 🌐 **Web**: [WidgetSuite.github.io/oraffle](https://WidgetSuite.github.io/oraffle/) -- 📱 **Android**: [Google Play Store](https://play.google.com/store/apps/details?id=es.victorcarreras.oraffle) +- 📱 **Android**: [Google Play Store](https://play.google.com/store/apps/details?id=es.victorcarreras.raffle-app) - 🪟 **Windows**: [Microsoft Store](https://apps.microsoft.com/store/detail/ORaffle) - 📱 **iOS**: [App Store](https://apps.apple.com/app/oraffle) - 🍎 **macOS**: [Mac App Store](https://apps.apple.com/app/oraffle) @@ -35,7 +35,7 @@ _ORaffle provides an intuitive interface for managing participants and picking w - **Bulk Import**: Paste a list of names (one per line) for rapid participant setup. - **Animated Winner Selection**: Exciting visual effects and animations during the drawing process. - **Multiple Winner Support**: Select multiple winners with automatic position tracking (1st, 2nd, 3rd place, etc.). -- **Custom Branding**: +- **Custom Branding**: - **Logo Integration**: Display your company or event logo in the app header. - **Theming**: Personalize the UI color scheme via custom hex codes to match your brand identity. - **Winner History**: Dedicated screen to view all selected winners with timestamps and rankings. @@ -56,12 +56,14 @@ ORaffle follows **Clean Architecture** principles, ensuring the codebase is main ### Installation 1. **Clone the repository** + ```bash git clone https://github.com/WidgetSuite/oraffle.git cd oraffle ``` 2. **Install dependencies** + ```bash flutter pub get ``` diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index f3c29ff..39816cc 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,3 +1,6 @@ +import java.util.Properties +import java.io.FileInputStream + plugins { id("com.android.application") id("kotlin-android") @@ -5,8 +8,14 @@ plugins { id("dev.flutter.flutter-gradle-plugin") } +val keystoreProperties = Properties() +val keystorePropertiesFile = rootProject.file("key.properties") +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) +} + android { - namespace = "com.example.oraffle" + namespace = "es.victorcarreras.oraffle" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -20,8 +29,7 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.oraffle" + applicationId = "es.victorcarreras.oraffle" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion @@ -30,11 +38,18 @@ android { versionName = flutter.versionName } + signingConfigs { + create("release") { + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["keyPassword"] as String + storeFile = keystoreProperties["storeFile"]?.let { file(it) } + storePassword = keystoreProperties["storePassword"] as String + } + } + buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.getByName("debug") + signingConfig = signingConfigs.getByName("release") } } } diff --git a/android/app/src/main/kotlin/com/example/oraffle/MainActivity.kt b/android/app/src/main/kotlin/es/victorcarreras/oraffle/MainActivity.kt similarity index 73% rename from android/app/src/main/kotlin/com/example/oraffle/MainActivity.kt rename to android/app/src/main/kotlin/es/victorcarreras/oraffle/MainActivity.kt index 68b0726..c1672dc 100644 --- a/android/app/src/main/kotlin/com/example/oraffle/MainActivity.kt +++ b/android/app/src/main/kotlin/es/victorcarreras/oraffle/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.oraffle +package es.victorcarreras.oraffle import io.flutter.embedding.android.FlutterActivity diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..620e46e --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8b5c267..a07a42d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -383,7 +383,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -399,7 +399,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -416,7 +416,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -431,7 +431,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -563,7 +563,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -586,7 +586,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index f1a4c64..38e5c27 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "oraffle") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.oraffle") +set(APPLICATION_ID "es.victorcarreras.raffle-app") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..ff5ddb3 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6bfe432..2dcbcd4 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -395,7 +395,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/oraffle.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/oraffle"; @@ -409,7 +409,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/oraffle.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/oraffle"; @@ -423,7 +423,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/oraffle.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/oraffle"; diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index 65b2868..0f64532 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = ORaffle // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.oraffle +PRODUCT_BUNDLE_IDENTIFIER = es.victorcarreras.raffle-app // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2026 es.victorcarreras. All rights reserved. diff --git a/pubspec.lock b/pubspec.lock index 1d5de29..5a3fd50 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -167,7 +167,7 @@ packages: source: hosted version: "9.1.1" flutter_launcher_icons: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_launcher_icons sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" diff --git a/pubspec.yaml b/pubspec.yaml index faa70be..4b581c7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: oraffle description: "A new Flutter project." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -48,7 +48,6 @@ dependencies: lucide_icons: ^0.257.0 path_provider: ^2.1.5 collection: ^1.19.1 - flutter_launcher_icons: ^0.14.4 dev_dependencies: flutter_test: @@ -61,6 +60,8 @@ dev_dependencies: # rules and activating additional ones. flutter_lints: ^6.0.0 + flutter_launcher_icons: "^0.14.4" + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -150,4 +151,4 @@ msix_config: languages: ar-sa, ca-es, de-de, en-GB, es-ES, eu-ES, fr-fr, gl-es, hi-in, it-it, ja-jp, pt-br, zh-cn capabilities: internetClient file_extension: .quiz - store: true \ No newline at end of file + store: true diff --git a/snap/gui/oraffle-app.desktop b/snap/gui/oraffle-app.desktop new file mode 100644 index 0000000..a0bb7b4 --- /dev/null +++ b/snap/gui/oraffle-app.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=ORaffle +Comment=A Raffle application +Exec=oraffle-app +Icon=${SNAP}/meta/gui/icon.png +Type=Application +Categories=Utility; +StartupNotify=true +StartupWMClass=oraffle diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 0000000..5f47832 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,29 @@ +name: oraffle-app +version: "1.0.0" +summary: ORaffle +description: | + A Raffle application. +base: core24 +confinement: strict +grade: stable + +apps: + oraffle-app: + command: oraffle + extensions: [gnome] + plugs: + - network + - home + +parts: + oraffle: + plugin: dump + source: build/linux/x64/release/bundle/ + + icon: + plugin: dump + source: images/ + organize: + oraffle.png: meta/gui/icon.png + prime: + - meta/gui/icon.png diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index 1c4586c..2a1c084 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -93,7 +93,7 @@ BEGIN VALUE "FileDescription", "ORaffle" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "ORaffle" "\0" - VALUE "LegalCopyright", "Copyright (C) 2026 com.example. All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 es.victorcarreras. All rights reserved." "\0" VALUE "OriginalFilename", "oraffle.exe" "\0" VALUE "ProductName", "ORaffle" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0"