chore: ビルド環境準備 (bin/ディレクトリ・スクリプト・テストデータを含む)

This commit is contained in:
joe 2026-03-06 12:19:37 +09:00
parent 0ac6edac9f
commit 665f2f6880
6 changed files with 281 additions and 37 deletions

56
.gitignore vendored
View file

@ -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
View 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'

View 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

View 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
View 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/

View file

@ -0,0 +1,70 @@
#!/bin/bash
# Version: 1.0.0
#
# 販売アシスト 1 号用の APK ビルドスクリプト
# 自動的な BUILD_TIMESTAMPUTC タイムスタンプを付与し、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
# 必要に応じて AABGoogle 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 "=== ビルド完了 ==="