Compare commits

...

18 Commits

Author SHA1 Message Date
stopflock
fe0f298c0e debug ASC API key 2025-10-16 12:35:30 -05:00
stopflock
0ac158eb4a Send to apple on successful tag build 2025-10-16 12:21:12 -05:00
stopflock
7eb680c677 Fin 2025-10-16 11:19:21 -05:00
stopflock
a30dace404 Build worked - now provisioning profile for .ipa packaging 2025-10-16 11:11:40 -05:00
stopflock
50d2c6cbf6 Brain wipe - take 2 2025-10-16 10:55:19 -05:00
stopflock
925804e546 <Sad Mac> Steve is dead :( 2025-10-16 10:18:48 -05:00
stopflock
4076d9657a Actually use keychain we create 2025-10-16 09:54:42 -05:00
stopflock
789930049a keychain hell 2025-10-16 09:48:32 -05:00
stopflock
09019915e7 Allow manual GH actions 2025-10-16 00:17:17 -05:00
stopflock
16e1927ff1 add debug output for ios builds 2025-10-16 00:05:22 -05:00
stopflock
02e43f78c3 Now trying to follow an actual guide 2025-10-15 23:39:51 -05:00
stopflock
8a109029ca Please lord apple codesigning 2025-10-15 23:20:38 -05:00
stopflock
cd5315b919 Specify team ID and provisioning profile for signing apple builds 2025-10-15 22:56:47 -05:00
stopflock
03f3419f72 Code signing for apple 2025-10-15 22:26:02 -05:00
stopflock
7ace123b4b auto-refresh suspected csv, put url in dev_config 2025-10-14 22:15:52 -05:00
stopflock
08f017fb0f TODOs 3 2025-10-13 17:04:03 -05:00
stopflock
7a199a3258 TODOs 2 2025-10-13 15:03:02 -05:00
stopflock
8c999c04cd TODOs 2025-10-13 14:55:55 -05:00
7 changed files with 144 additions and 16 deletions

View File

@@ -3,6 +3,7 @@ on:
push:
tags:
- '*'
workflow_dispatch:
permissions:
contents: write
@@ -146,13 +147,89 @@ jobs:
dart run flutter_launcher_icons
dart run flutter_native_splash:create
# - name: Build iOS .ipa
# run: flutter build ipa --release
- name: Build iOS .app
- name: Install Apple certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_DISTRIBUTION_CERTIFICATE_BASE64 }}
P12_PASSWORD: ""
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_APPSTORE_PROVISIONING_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
run: |
flutter build ios --release --no-codesign --dart-define=OSM_PROD_CLIENTID='${{ secrets.OSM_PROD_CLIENTID }}' --dart-define=OSM_SANDBOX_CLIENTID='${{ secrets.OSM_SANDBOX_CLIENTID }}'
./app2ipa.sh build/ios/iphoneos/Runner.app
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
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
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_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 set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Set this keychain as the default
security list-keychain -d user -s $KEYCHAIN_PATH
security default-keychain -s $KEYCHAIN_PATH
# install provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/61f9fdb9-bf2d-4d94-b249-63155ee71e74.mobileprovision
# Also install using the profile's internal UUID for better compatibility
UUID=$(security cms -D -i $PP_PATH | plutil -extract UUID xml1 -o - - | xmllint --xpath "//string/text()" -)
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/$UUID.mobileprovision
# Debug: Check what we actually have
echo "=== Certificates in keychain ==="
security find-identity -v -p codesigning $KEYCHAIN_PATH
echo "=== Provisioning profiles ==="
ls -la ~/Library/MobileDevice/Provisioning\ Profiles/
echo "=== Profile UUID extracted: $UUID ==="
- name: Create export options
run: |
cat > ios/exportOptions.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>7XG8T28436</string>
<key>provisioningProfiles</key>
<dict>
<key>me.deflock.deflockapp</key>
<string>61f9fdb9-bf2d-4d94-b249-63155ee71e74</string>
</dict>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
</dict>
</plist>
EOF
- name: Build iOS .ipa
run: |
flutter build ipa --release \
--export-options-plist=ios/exportOptions.plist \
--dart-define=OSM_PROD_CLIENTID='${{ secrets.OSM_PROD_CLIENTID }}' \
--dart-define=OSM_SANDBOX_CLIENTID='${{ secrets.OSM_SANDBOX_CLIENTID }}'
cp build/ios/ipa/*.ipa Runner.ipa
- name: Clean up keychain and provisioning profile
if: always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
rm ~/Library/MobileDevice/Provisioning\ Profiles/61f9fdb9-bf2d-4d94-b249-63155ee71e74.mobileprovision
- name: Upload IPA artifact
uses: actions/upload-artifact@v4
@@ -161,6 +238,37 @@ jobs:
path: Runner.ipa
if-no-files-found: 'error'
- name: Upload to App Store Connect
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
run: |
# Create the private keys directory
mkdir -p ~/private_keys
# Decode and save the API key
echo -n "$APP_STORE_CONNECT_API_KEY_BASE64" | base64 --decode > ~/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8
# Debug: Check if file was created and has content
echo "=== API Key File Debug ==="
ls -la ~/private_keys/
file ~/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8
echo "First few lines of key file:"
head -3 ~/private_keys/AuthKey_${APP_STORE_CONNECT_API_KEY_ID}.p8
echo "=========================="
# Upload using xcrun altool
xcrun altool --upload-app \
--type ios \
--file Runner.ipa \
--apiKey $APP_STORE_CONNECT_API_KEY_ID \
--apiIssuer $APP_STORE_CONNECT_ISSUER_ID
# Clean up
rm -rf ~/private_keys
attach-to-release:
name: Attach Assets to Release
needs: [get-version, build-android-apk, build-android-aab, build-ios]

View File

@@ -79,7 +79,9 @@ cp lib/keys.dart.example lib/keys.dart
## Roadmap
### Needed Bugfixes
- Are offline areas really working? Are they preferred for fast loading even when online? Check working.
- Are offline areas preferred for fast loading even when online? Check working.
- Ease up on overpass by pre-caching a larger area. Maybe we could grab the full latest database just like for suspected locations?
- Stop failing to fetch tiles; keep retrying after 3. Remove kTileFetchInitialDelayMs, kTileFetchJitter1Ms, etc from dev_config. Fix network indicator - only done when fetch queue is empty!
### Current Development
- Swap in alprwatch.org/directions avoidance routing API

View File

@@ -470,7 +470,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 7XG8T28436;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -479,6 +482,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = me.deflock.deflockapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "61f9fdb9-bf2d-4d94-b249-63155ee71e74";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -652,7 +656,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 7XG8T28436;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -661,6 +668,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = me.deflock.deflockapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "61f9fdb9-bf2d-4d94-b249-63155ee71e74";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -674,7 +682,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 7XG8T28436;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -683,6 +694,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = me.deflock.deflockapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "61f9fdb9-bf2d-4d94-b249-63155ee71e74";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

View File

@@ -165,7 +165,7 @@ class AppState extends ChangeNotifier {
await _operatorProfileState.init();
await _profileState.init();
await _suspectedLocationState.init();
await _suspectedLocationState.init(offlineMode: _settingsState.offlineMode);
await _uploadQueueState.init();
await _authState.init(_settingsState.uploadMode);

View File

@@ -40,6 +40,9 @@ double bottomPositionFromButtonBar(double spacingAboveButtonBar, double safeArea
const String kClientName = 'DeFlock';
// Note: Version is now dynamically retrieved from VersionService
// Suspected locations CSV URL
const String kSuspectedLocationsCsvUrl = 'https://alprwatch.org/pub/flock_utilities_mini_latest.csv';
// Development/testing features - set to false for production builds
const bool kEnableDevelopmentModes = false; // Set to false to hide sandbox/simulate modes and force production mode

View File

@@ -7,6 +7,7 @@ import 'package:latlong2/latlong.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:csv/csv.dart';
import '../dev_config.dart';
import '../models/suspected_location.dart';
import 'suspected_location_cache.dart';
@@ -15,7 +16,6 @@ class SuspectedLocationService {
factory SuspectedLocationService() => _instance;
SuspectedLocationService._();
static const String _csvUrl = 'https://alprwatch.org/pub/flock_utilities_mini_latest.csv';
static const String _prefsKeyEnabled = 'suspected_locations_enabled';
static const Duration _maxAge = Duration(days: 7);
static const Duration _timeout = Duration(seconds: 30);
@@ -34,15 +34,18 @@ class SuspectedLocationService {
bool get isLoading => _isLoading;
/// Initialize the service - load from storage and check if refresh needed
Future<void> init() async {
Future<void> init({bool offlineMode = false}) async {
await _loadFromStorage();
// Load cache data
await _cache.loadFromStorage();
// Only auto-fetch if enabled and data is stale or missing
if (_isEnabled && _shouldRefresh()) {
// Only auto-fetch if enabled, data is stale or missing, and we are not offline
if (_isEnabled && _shouldRefresh() && !offlineMode) {
debugPrint('[SuspectedLocationService] Auto-refreshing CSV data on startup (older than $_maxAge or missing)');
await _fetchData();
} else if (_isEnabled && _shouldRefresh() && offlineMode) {
debugPrint('[SuspectedLocationService] Skipping auto-refresh due to offline mode - data is ${_cache.lastFetchTime != null ? 'outdated' : 'missing'}');
}
}
@@ -100,10 +103,10 @@ class SuspectedLocationService {
_isLoading = true;
try {
onProgress?.call('Downloading CSV data...', null);
debugPrint('[SuspectedLocationService] Fetching CSV data from $_csvUrl');
debugPrint('[SuspectedLocationService] Fetching CSV data from $kSuspectedLocationsCsvUrl');
final response = await http.get(
Uri.parse(_csvUrl),
Uri.parse(kSuspectedLocationsCsvUrl),
headers: {
'User-Agent': 'DeFlock/1.0 (OSM surveillance mapping app)',
},

View File

@@ -37,8 +37,8 @@ class SuspectedLocationState extends ChangeNotifier {
DateTime? get lastFetchTime => _service.lastFetchTime;
/// Initialize the state
Future<void> init() async {
await _service.init();
Future<void> init({bool offlineMode = false}) async {
await _service.init(offlineMode: offlineMode);
notifyListeners();
}