chore: ビルド環境準備 (bin/ディレクトリ・スクリプト・テストデータを含む)
This commit is contained in:
parent
0ac6edac9f
commit
665f2f6880
6 changed files with 281 additions and 37 deletions
56
.gitignore
vendored
56
.gitignore
vendored
|
|
@ -3,46 +3,28 @@
|
||||||
*.log
|
*.log
|
||||||
*.pyc
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
|
||||||
.build/
|
|
||||||
.buildlog/
|
|
||||||
.history
|
|
||||||
.svn/
|
|
||||||
.swiftpm/
|
|
||||||
migrate_working_dir/
|
|
||||||
|
|
||||||
# IntelliJ related
|
# Dart
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
|
||||||
**/doc/api/
|
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
.dart_tool/
|
||||||
.flutter-plugins-dependencies
|
packages/
|
||||||
.pub-cache/
|
pubspec.lock
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
/coverage/
|
|
||||||
|
|
||||||
# Symbolication related
|
# Build outputs
|
||||||
app.*.symbols
|
build/
|
||||||
|
ios/iPhone*.xcodeproj/
|
||||||
|
ios/Runner.xcworkspace/
|
||||||
|
ios/Pods/
|
||||||
|
libappbundle/
|
||||||
|
*.apk
|
||||||
|
*.aab
|
||||||
|
|
||||||
# Obfuscation related
|
# Flutter tool
|
||||||
app.*.map.json
|
.flutter-plugins
|
||||||
|
.flutter-version
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
# Environment variables
|
||||||
/android/app/debug
|
.env
|
||||||
/android/app/profile
|
.env.local
|
||||||
/android/app/release
|
|
||||||
|
|
||||||
# Local SQLite snapshots
|
|
||||||
app.db
|
|
||||||
16
analysis_options.yaml
Normal file
16
analysis_options.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
- prefer_const_constructors
|
||||||
|
- prefer_const_declarations
|
||||||
|
- avoid_unnecessary_containers
|
||||||
|
- prefer_single_quotes
|
||||||
|
- sort_child_properties_last
|
||||||
|
- prefer_final_locals
|
||||||
|
- avoid_print
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- '**/*.g.dart'
|
||||||
|
- '**/*.freezed.dart'
|
||||||
18
data/mothership/pubspec.yaml
Normal file
18
data/mothership/pubspec.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Version: 1.0.0
|
||||||
|
name: mothership_server
|
||||||
|
description: 母艦「お局様」用 HTTP サーバーアプリケーション
|
||||||
|
publish_to: 'none'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
http: ^1.1.0
|
||||||
|
uuid: ^4.3.3
|
||||||
|
json_annotation: ^4.9.0
|
||||||
|
path_provider: ^2.1.1
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
build_runner: ^2.4.0
|
||||||
|
json_serializable: ^6.8.0
|
||||||
|
http_mock_adapter: ^0.7.0
|
||||||
126
lib/utils/build_expiry_info.dart
Normal file
126
lib/utils/build_expiry_info.dart
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Version: 1.0.0
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
/// アプリのビルド lifetime を管理するクラス
|
||||||
|
class BuildExpiryInfo {
|
||||||
|
/// バリッドな状態(90 日以内)
|
||||||
|
static const String statusValid = 'valid';
|
||||||
|
|
||||||
|
/// 期限切れ状態
|
||||||
|
static const String statusExpired = 'expired';
|
||||||
|
|
||||||
|
/// ビルド時間を UTC 秒数として受け取り、残存寿命を判定する
|
||||||
|
/// [timestamp] は UTC タイムスタンプ(秒)
|
||||||
|
String getStatus(int timestamp) {
|
||||||
|
final now = DateTime.now().toUtc();
|
||||||
|
final buildTime = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
timestamp * 1000,
|
||||||
|
isUtc: true,
|
||||||
|
);
|
||||||
|
final expiryTime = _expiryTimestamp.buildDateTime;
|
||||||
|
|
||||||
|
if (now.isAfter(expiryTime)) {
|
||||||
|
return statusExpired;
|
||||||
|
}
|
||||||
|
return statusValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 残り寿命を DateTime オブジェクトとして返す(期限切れの場合 null)
|
||||||
|
DateTime? getRemainingExpiry(int timestamp) {
|
||||||
|
final now = DateTime.now().toUtc();
|
||||||
|
final expiryTime = _expiryTimestamp.buildDateTime;
|
||||||
|
|
||||||
|
if (now.isAfter(expiryTime)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 残り寿命を計算(例:90 日 + 15 日間延命用 buffer)
|
||||||
|
final daysRemaining = ((expiryTime.millisecondsSinceEpoch - now.millisecondsSinceEpoch) ~/ Duration.millisecondsPerDay);
|
||||||
|
return now.add(Duration(days: daysRemaining));
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuildExpiryInfo get instance => _instance;
|
||||||
|
factory BuildExpiryInfo() => _instance;
|
||||||
|
|
||||||
|
late final DateTime _expiryTimestamp;
|
||||||
|
String _appBuildTimestamp = '';
|
||||||
|
|
||||||
|
/// アプリのパッケージ情報からビルド時間を取得する
|
||||||
|
void initializeFromPackageInfo(PackageInfo packageInfo) async {
|
||||||
|
_appBuildTimestamp = (await packageInfo).version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 寿命切れ画面を表示すべきかどうかを判定
|
||||||
|
bool get isExpired => getStatus(_parseTimestamp(_appBuildTimestamp));
|
||||||
|
|
||||||
|
/// 寿命切れメッセージの取得
|
||||||
|
String get expiredMessage {
|
||||||
|
return '本アプリの有効期限が切れています。\n母艦(お局様)からの同期が必要となります。';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// アプリ初期化時に呼ぶヘルパー
|
||||||
|
class ExpiryInitHelper {
|
||||||
|
static Future<void> initialize(BuildContext context, PackageInfo packageInfo) async {
|
||||||
|
final expiry = BuildExpiryInfo();
|
||||||
|
|
||||||
|
// パッケージ情報からビルド時間を含むバージョンをパース(例: "1.0.0+1234567890")
|
||||||
|
final timestamp = int.tryParse(_extractTimestamp(packageInfo.version));
|
||||||
|
|
||||||
|
if (timestamp != null) {
|
||||||
|
expiry._expiryTimestamp = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
timestamp * 1000,
|
||||||
|
isUtc: true,
|
||||||
|
).add(const Duration(days: 90));
|
||||||
|
|
||||||
|
// トランザクションで寿命チェックを実行
|
||||||
|
await context.mount<ExpiredApp>(
|
||||||
|
key: ExpiryInitHelper._routeName,
|
||||||
|
condition: () => expiry.isExpired,
|
||||||
|
builder: (context) => ExpiredApp(expiry: expiry),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _extractTimestamp(String version) {
|
||||||
|
// "1.0.0+1234567890" の形式の場合、+以降をパース
|
||||||
|
final parts = version.split('+');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
return parts[1];
|
||||||
|
}
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static const String _routeName = '/expired_app_route';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExpiredApp extends StatelessWidget {
|
||||||
|
final BuildExpiryInfo expiry;
|
||||||
|
const ExpiredApp({super.key, required this.expiry});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('有効期限切れ')),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.error_outline, size: 80, color: Colors.red),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Text(
|
||||||
|
expiry.expiredMessage,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false),
|
||||||
|
child: const Text('設定画面へ戻る'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
pubspec.yaml
Normal file
32
pubspec.yaml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: sales_assist_1
|
||||||
|
description: オフライン単体で見積・納品・請求・レジ業務まで完結できる販売アシスタント
|
||||||
|
publish_to: 'none'
|
||||||
|
|
||||||
|
version: 1.0.0+4
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
cupertino_icons: ^1.0.6
|
||||||
|
|
||||||
|
# SQLite データ永続化
|
||||||
|
sqflite: ^2.3.3
|
||||||
|
path_provider: ^2.1.1
|
||||||
|
|
||||||
|
# Google エコシステム連携
|
||||||
|
google_sign_in: ^6.1.0
|
||||||
|
http: ^1.1.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^3.0.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
|
||||||
|
assets:
|
||||||
|
- assets/
|
||||||
70
scripts/build_with_expiry.sh
Normal file
70
scripts/build_with_expiry.sh
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Version: 1.0.0
|
||||||
|
#
|
||||||
|
# 販売アシスト 1 号用の APK ビルドスクリプト
|
||||||
|
# 自動的な BUILD_TIMESTAMP(UTC タイムスタンプ)を付与し、90 日寿命チェックされた APK を生成
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
FLUTTER="${FLUTTER:-flutter}"
|
||||||
|
BUILD_TYPE="${1:-release}" # debug|profile|release(デフォルト:release)
|
||||||
|
|
||||||
|
echo "=== 販売アシスト 1 号 APK ビルドスクリプト ==="
|
||||||
|
echo "ビルドモード: ${BUILD_TYPE}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# プロジェクトディレクトリへ cd
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
# パッケージ名を取得
|
||||||
|
PACKAGE_NAME=$(grep '^name:' pubspec.yaml | cut -d' ' -f2)
|
||||||
|
|
||||||
|
# 環境チェック
|
||||||
|
if [ ! -f "pubspec.lock" ]; then
|
||||||
|
echo "[エラー] pubspec.lock を発見できません。flutter pub get を実行してください。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[情報] パッケージ名: ${PACKAGE_NAME}"
|
||||||
|
|
||||||
|
# flutter analyze を実行(オプション:--no-fatal-warnings は必要に応じて)
|
||||||
|
echo "[実装] flutter analyze..."
|
||||||
|
$FLUTTER analyze --no-fatal-warnings || true # エラーを警告として扱う
|
||||||
|
|
||||||
|
# ビルドタインプスタンプの自動付与
|
||||||
|
BUILD_TIMESTAMP=$(date -u +%s)
|
||||||
|
DART_DEFINE="--dart-define=APP_BUILD_TIMESTAMP=${BUILD_TIMESTAMP}"
|
||||||
|
|
||||||
|
echo "[情報] BUILD_TIMESTAMP: ${BUILD_TIMESTAMP}"
|
||||||
|
echo " 有効期限(UTC): $(date -u -d "@${BUILD_TIMESTAMP}" "+%Y-%m-%d %H:%M:%S")"
|
||||||
|
echo " 90 日後の期限:$(date -u -d "@${BUILD_TIMESTAMP} + 90 days" "+%Y-%m-%d %H:%M:%S")"
|
||||||
|
|
||||||
|
# APK ビルド実行(リリースビルドモードが推奨)
|
||||||
|
echo ""
|
||||||
|
echo "[実装] flutter build apk ${DART_DEFINE} --release..."
|
||||||
|
$FLUTTER build apk $DART_DEFINE \
|
||||||
|
--dart-define=APP_BUILD_TIMESTAMP=$BUILD_TIMESTAMP \
|
||||||
|
--release \
|
||||||
|
--build-number=$((date +%s)) # ビルド番号を秒数(ビルド毎に異なる)
|
||||||
|
|
||||||
|
# バイナリ出力先を確認
|
||||||
|
APK_PATH=""
|
||||||
|
if [ -f "$PROJECT_DIR/build/app/outputs/flutter-apk/app-release.apk" ]; then
|
||||||
|
APK_PATH="$PROJECT_DIR/build/app/outputs/flutter-apk/app-release.apk"
|
||||||
|
elif [ -f "$PROJECT_DIR/build/app/outputs/bundle/release/app-release.aab" ]; then
|
||||||
|
# 必要に応じて AAB(Google Play)も出力可
|
||||||
|
APK_PATH="$PROJECT_DIR/build/app/outputs/bundle/release/app-release.aab"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$APK_PATH" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "[成功] APK 生成完了:"
|
||||||
|
echo " ファイル: ${APK_PATH}"
|
||||||
|
echo " サイズ:$(ls -lh "$APK_PATH" | awk '{print $5}')"
|
||||||
|
else
|
||||||
|
echo "[警告] バイナリ出力先が見つかりませんでした。"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== ビルド完了 ==="
|
||||||
Loading…
Reference in a new issue