# Version from pubspec.yaml (without build number) VERSION := $(shell grep '^version:' pubspec.yaml | sed 's/version: *//;s/+.*//') # Default target .PHONY: help help: @echo "Flutter project commands:" @echo "" @echo " Setup:" @echo " get Install dependencies" @echo " clean Clean build artifacts" @echo " install-hooks Install git hooks via lefthook" @echo " pods Update CocoaPods repo and install pods" @echo "" @echo " i18n:" @echo " i18n Build i18n generated Dart code" @echo " i18n-watch Watch and rebuild i18n on changes" @echo "" @echo " Development:" @echo " run Run the app in debug mode" @echo " format Format all Dart files" @echo " analyze Analyze all Dart files" @echo " check Check all files (format + analyze, no changes)" @echo "" @echo " Testing:" @echo " test Run all tests" @echo " test-coverage Run tests with coverage report" @echo "" @echo " API:" @echo " fetch-openapi Fetch openapi.json from chenasraf/nextcloud-pantry" @echo "" @echo " Assets:" @echo " icons Generate launcher icons, favicon & web logo from SVG" @echo " splash Generate splash screen from SVG" @echo "" @echo " Building:" @echo " android-install Build APK and install on connected device" @echo " android-build-apk Build Android APK" @echo " android-build-apk-split Build Android split-per-ABI APKs" @echo " android-build-aab Build Android App Bundle" @echo " android-push Build APK and push to device via adb" @echo " ios-build Build iOS (no codesign)" @echo " macos-build Build macOS app (.app bundle, no codesign)" @echo " macos-build-pkg Build signed macOS .pkg for App Store" @echo " build-all Build all platforms" @echo "" @echo " Release:" @echo " android-release-apk Build APK and copy to build/release/" @echo " android-release-aab Build AAB and copy to build/release/" @echo " ios-release Build IPA and copy to build/release/" @echo " macos-release Build PKG and copy to build/release/" @echo " release-all Build and release all platforms" @echo "" @echo " Deploying:" @echo " android-deploy Build AAB and upload to Google Play (TRACK=internal|beta|production, STATUS=draft|completed)" @echo " android-promote Promote release between tracks (FROM=internal, TO=production, STATUS=draft|completed)" @echo " ios-deploy Build IPA and upload (DEST=testflight|appstore, default: testflight)" @echo " ios-submit Submit the existing App Store build for review (no upload)" @echo " macos-deploy Build PKG and upload (DEST=testflight|appstore, default: testflight)" @echo " macos-submit Submit the existing Mac App Store build for review (no upload)" @echo " deploy-production Build and deploy to production (Google Play + App Store)" @echo " deploy-beta Build and deploy to beta (Google Play beta + TestFlight)" # Setup .PHONY: get get: flutter pub get .PHONY: clean clean: flutter clean rm -rf coverage/ .PHONY: build-clean build-clean: rm -rf build/release/* # i18n .PHONY: i18n i18n: dart run tool/fix_i18n_escapes.dart dart run build_runner build --delete-conflicting-outputs .PHONY: i18n-watch i18n-watch: dart run build_runner watch --delete-conflicting-outputs # Development .PHONY: run run: flutter run .PHONY: format format: dart format . .PHONY: analyze analyze: flutter analyze --no-fatal-infos .PHONY: check check: dart format --output=none --set-exit-if-changed . flutter analyze --no-fatal-infos # Testing .PHONY: test test: ifdef FILES flutter test $(FILES) else flutter test endif .PHONY: test-coverage test-coverage: flutter test --coverage @echo "Coverage report generated at coverage/lcov.info" # Building .PHONY: android-build-apk android-build-apk: flutter build apk --release --obfuscate --split-debug-info=build/debug-info-apk .PHONY: android-build-apk-split android-build-apk-split: flutter build apk --release --split-per-abi --obfuscate --split-debug-info=build/debug-info-apk .PHONY: android-install android-install: android-build-apk flutter install .PHONY: android-push android-push: android-build-apk adb push build/app/outputs/flutter-apk/app-release.apk /sdcard/Download/pantry-$(VERSION).apk @echo "-> /sdcard/Download/pantry-$(VERSION).apk" .PHONY: android-build-aab android-build-aab: flutter build appbundle --release --obfuscate --split-debug-info=build/debug-info-aab .PHONY: ios-build ios-build: flutter build ios --release --no-codesign --obfuscate --split-debug-info=build/debug-info-ios .PHONY: ios-build-ipa ios-build-ipa: flutter build ipa --release --obfuscate --split-debug-info=build/debug-info-ios --dart-define-from-file=.env --export-options-plist=ios/ExportOptions.plist .PHONY: macos-build macos-build: flutter build macos --release --obfuscate --split-debug-info=build/debug-info-macos .PHONY: macos-build-pkg macos-build-pkg: flutter build macos --config-only --obfuscate --split-debug-info=build/debug-info-macos rm -rf build/macos/Runner.xcarchive build/macos/pkg xcodebuild -workspace macos/Runner.xcworkspace \ -scheme Runner \ -configuration Release \ -archivePath build/macos/Runner.xcarchive \ -allowProvisioningUpdates \ archive xcodebuild -exportArchive \ -archivePath build/macos/Runner.xcarchive \ -exportPath build/macos/pkg \ -exportOptionsPlist macos/ExportOptions.plist \ -allowProvisioningUpdates .PHONY: build-all build-all: android-build-apk android-build-aab # Release (build + copy renamed artifacts to build/release/) .PHONY: android-release-apk android-release-apk: android-build-apk mkdir -p build/release cp build/app/outputs/flutter-apk/app-release.apk build/release/pantry-$(VERSION).apk @echo "-> build/release/pantry-$(VERSION).apk" .PHONY: android-release-aab android-release-aab: android-build-aab mkdir -p build/release cp build/app/outputs/bundle/release/app-release.aab build/release/pantry-$(VERSION).aab @echo "-> build/release/pantry-$(VERSION).aab" .PHONY: ios-release ios-release: ios-build-ipa mkdir -p build/release cp build/ios/ipa/*.ipa build/release/pantry-$(VERSION).ipa @echo "-> build/release/pantry-$(VERSION).ipa" .PHONY: macos-release macos-release: macos-build-pkg mkdir -p build/release cp build/macos/pkg/*.pkg build/release/pantry-$(VERSION).pkg @echo "-> build/release/pantry-$(VERSION).pkg" .PHONY: android-upload android-upload: @echo "$(or $(TRACK),beta)" | grep -qE '^(internal|alpha|beta|production)$$' || (echo "Error: Invalid TRACK '$(TRACK)'. Must be: internal, alpha, beta, production"; exit 1) @echo "$(or $(STATUS),draft)" | grep -qE '^(draft|completed|halted|inProgress)$$' || (echo "Error: Invalid STATUS '$(STATUS)'. Must be: draft, completed, halted, inProgress"; exit 1) @echo "Track: $(or $(TRACK),internal) | Status: $(or $(STATUS),draft)" bundle exec fastlane deploy track:$(or $(TRACK),internal) status:$(or $(STATUS),draft) .PHONY: android-deploy android-deploy: android-build-aab android-upload .PHONY: android-promote android-promote: @echo "$(or $(FROM),internal)" | grep -qE '^(internal|alpha|beta|production)$$' || (echo "Error: Invalid FROM '$(FROM)'. Must be: internal, alpha, beta, production"; exit 1) @echo "$(or $(TO),production)" | grep -qE '^(internal|alpha|beta|production)$$' || (echo "Error: Invalid TO '$(TO)'. Must be: internal, alpha, beta, production"; exit 1) @echo "$(or $(STATUS),draft)" | grep -qE '^(draft|completed|halted|inProgress)$$' || (echo "Error: Invalid STATUS '$(STATUS)'. Must be: draft, completed, halted, inProgress"; exit 1) @echo "Promote: $(or $(FROM),internal) -> $(or $(TO),production) | Status: $(or $(STATUS),draft)" bundle exec fastlane promote from:$(or $(FROM),internal) to:$(or $(TO),production) status:$(or $(STATUS),draft) .PHONY: ios-upload ios-upload: @echo "$(or $(DEST),testflight)" | grep -qE '^(testflight|appstore)$$' || (echo "Error: Invalid DEST '$(DEST)'. Must be: testflight, appstore"; exit 1) @echo "Destination: $(or $(DEST),testflight)" @if [ "$(or $(DEST),testflight)" = "appstore" ]; then \ bundle exec fastlane ios release; \ else \ bundle exec fastlane ios beta; \ fi .PHONY: ios-deploy ios-deploy: ios-build-ipa ios-upload .PHONY: ios-submit ios-submit: bundle exec fastlane ios submit .PHONY: macos-upload macos-upload: @echo "$(or $(DEST),testflight)" | grep -qE '^(testflight|appstore)$$' || (echo "Error: Invalid DEST '$(DEST)'. Must be: testflight, appstore"; exit 1) @echo "Destination: $(or $(DEST),testflight)" @if [ "$(or $(DEST),testflight)" = "appstore" ]; then \ bundle exec fastlane mac release; \ else \ bundle exec fastlane mac beta; \ fi .PHONY: macos-deploy macos-deploy: macos-build-pkg macos-upload .PHONY: macos-submit macos-submit: bundle exec fastlane mac submit .PHONY: release-all release-all: android-release-apk android-release-aab .PHONY: deploy-production deploy-production: $(MAKE) android-deploy TRACK=production STATUS=completed $(MAKE) ios-deploy DEST=appstore $(MAKE) macos-deploy DEST=appstore .PHONY: deploy-beta deploy-beta: $(MAKE) android-deploy TRACK=beta STATUS=completed $(MAKE) ios-deploy DEST=testflight $(MAKE) macos-deploy DEST=testflight # CocoaPods .PHONY: pods pods: cd ios && pod install --repo-update cd macos && pod install --repo-update # Git hooks .PHONY: install-hooks install-hooks: lefthook install # API .PHONY: fetch-openapi fetch-openapi: gh api repos/chenasraf/nextcloud-pantry/contents/openapi.json --jq '.content' | base64 -d > openapi.json @echo "-> openapi.json updated" # Assets .PHONY: copy-graphics copy-graphics: ifndef GRAPHICS_DIR $(error GRAPHICS_DIR is required. Usage: make copy-graphics GRAPHICS_DIR=~/path/to/graphics) endif @for pattern in icon logo; do \ for ext in svg png; do \ for f in $(GRAPHICS_DIR)/$$pattern*.$$ext; do \ [ -e "$$f" ] && cp "$$f" assets/icon/ && echo "Copied $$f" || true; \ done; \ done; \ done .PHONY: icons icons: mkdir -p assets/icon rsvg-convert -w 1024 -h 1024 assets/logo_icon_squircle.svg > assets/icon/icon.png rsvg-convert -w 1024 -h 1024 assets/logo_icon_square.svg > assets/icon/icon_ios.png rsvg-convert -w 1024 -h 1024 assets/logo_icon_foreground.svg > assets/icon/icon_foreground.png rsvg-convert -w 1024 -h 1024 assets/logo_icon_macos.svg > assets/icon/icon_macos.png dart run flutter_launcher_icons rsvg-convert -w 512 -h 512 assets/logo_icon_squircle.svg > fastlane/metadata/android/en-US/images/icon.png .PHONY: splash splash: mkdir -p assets/icon rsvg-convert -h 1152 --page-width 1920 --page-height 1920 --top 384 --left 384 assets/logo_icon.svg > assets/icon/splash.png dart run flutter_native_splash:create