Dart パッケージ管理 — pub エコシステムと依存関係
依存関係管理はプロジェクトのサプライチェーンである — うまく管理して初めてプロジェクトが安定する。
1. 学べること
- 完全な pubspec.yaml 設定:dependencies / dev_dependencies / dependency_overrides
- バージョン制約構文:
^/>=/anyとセマンティックバージョニング - pub.dev でのパッケージ検索、評価、選択
- プライベートパッケージホスティングと git 依存関係
- Bob のシナリオ:DataPipeline の依存関係設定
2. 開発者のリアルな物語
(1) 課題:依存関係のバージョン競合によるビルド失敗
Alice のチームは DataPipeline プロジェクトで http: ^1.1.0 を使っていたが、別の依存関係 api_client は http: >=0.13.0 <1.0.0 を要求していた。バージョン制約が互換性なく、dart pub get が失敗した。さらに悪いことに、依存関係がマイナー版をサイレントに更新し、重大な変更が導入されて CI ビルドが失敗した。チームは調査に 2 日を費やした。
(2) 解決策:セマンティックバージョニング
Dart はセマンティックバージョニング(SemVer)とバージョン制約構文を使って依存関係管理を予測可能にする。^1.2.0 は >=1.2.0 <2.0.0 を意味し、互換性を保証する。
dependencies:
http: ^1.2.0 # 1.x と互換、安全なマイナー/パッチ更新
args: ^2.4.2 # 2.x と互換
csv: ^6.0.0 # 6.x と互換
(3) 効果
- バージョン制約により依存関係のアップグレードが安全で制御可能になる
- pub.dev の pub points 評価システムが質の高いパッケージ選択を助ける
dependency_overridesが一時的にバージョン競合を解決する
3. 完全な pubspec.yaml 設定
(1) 設定構造
▶ サンプル:完全な pubspec.yaml
name: datapipeline
description: A CLI tool for e-commerce data analytics processing million-level orders
version: 1.0.0
homepage: https://github.com/bob/datapipeline
repository: https://github.com/bob/datapipeline
documentation: https://datapipeline.dev/docs
environment:
sdk: ^3.0.0
dependencies:
# CLI 引数解析
args: ^2.4.2
# API 呼び出し用 HTTP クライアント
http: ^1.2.0
# CSV ファイルパース
csv: ^6.0.0
# SQLite データベースサポート
sqlite3: ^2.4.0
# パス操作ユーティリティ
path: ^1.9.0
# ロギングフレームワーク
logging: ^1.2.0
# YAML 設定パース
yaml: ^3.1.2
dev_dependencies:
# テストフレームワーク
test: ^1.24.0
# コード生成ランナー
build_runner: ^2.4.0
# JSON シリアライゼーション
json_serializable: ^6.7.0
# Lint ルール
lints: ^3.0.0
dependency_overrides:
# 一時的:バージョン競合の解決
# transitive: ^1.0.0
executables:
datapipeline: datapipeline
| フィールド | 必須 | 説明 |
|---|---|---|
name |
はい | パッケージ名(小文字 + アンダースコア) |
description |
はい | パッケージ説明(60-180 文字) |
version |
いいえ | セマンティックバージョン番号 |
environment |
はい | SDK バージョン制約 |
dependencies |
いいえ | ランタイム依存関係 |
dev_dependencies |
いいえ | 開発時依存関係 |
dependency_overrides |
いいえ | 特定バージョンの強制 |
4. バージョン制約構文
(1) セマンティックバージョニング
▶ サンプル:バージョン制約構文
dependencies:
# キャレット構文: ^1.2.3 = >=1.2.3 <2.0.0
package_a: ^1.2.3
# 範囲構文
package_b: ">=1.2.3 <2.0.0"
# 最小バージョン
package_c: ">=1.2.3"
# 任意バージョン(危険!)
package_d: any
# 厳密バージョン
package_e: "1.2.3"
# Git 依存関係
package_f:
git:
url: https://github.com/user/package_f.git
ref: main
# パス依存関係(ローカル開発)
package_g:
path: ../package_g
| 構文 | 意味 | 例 | 安全性 |
|---|---|---|---|
^1.2.3 |
>=1.2.3 <2.0.0 |
最も一般的 | 高 |
>=1.2.3 <2.0.0 |
範囲制約 | 精密な制御 | 高 |
>=1.2.3 |
最小バージョン | リスク高 | 中 |
any |
任意バージョン | 非推奨 | 低 |
1.2.3 |
厳密バージョン | ピン留め | 高(柔軟性なし) |
(2) バージョン解決ルール
| SemVer ルール | 説明 | 例 |
|---|---|---|
| メジャーバージョン | 非互換 API 変更 | 1.x → 2.x |
| マイナーバージョン | 後方互換の新機能 | 1.2 → 1.3 |
| パッチバージョン | 後方互換のバグ修正 | 1.2.3 → 1.2.4 |
▶ サンプル:バージョン競合と解決
# シナリオ:package_a は http ^0.13.0 を要求、package_b は http ^1.0.0 を要求
# これはメジャーバージョンの競合 - 互換性なし!
# 解決策 1:package_a を http ^1.0.0 をサポートするバージョンに更新
# 解決策 2:dependency_overrides を使用(最後の手段)
dependencies:
http: ^1.2.0
dependency_overrides:
http: ^1.2.0 # 特定バージョンを強制
5. pub.dev でのパッケージ評価
(1) 評価基準
| 観点 | メトリック | 重み |
|---|---|---|
| Pub Points | プラットフォーム対応/ドキュメント/依存関係健全性 | 高 |
| Likes | コミュニティ認知度 | 中 |
| 人気度 | 使用数 | 中 |
| Pub Verified | パブリッシャー認証済み | 高 |
| 最近の更新 | メンテナンス活動 | 高 |
| プラットフォーム | サポートされるプラットフォーム | 必要に応じて |
▶ サンプル:DataPipeline パッケージ選択
# DataPipeline パッケージ選択基準:
#
# args (pub points: 140/140, likes: 300+)
# - 公式 Dart チームパッケージ
# - 安定した API、よく文書化されている
# - CLI 引数解析に最適
#
# http (pub points: 140/140, likes: 1000+)
# - 公式 Dart チームパッケージ
# - 標準 HTTP クライアント
# - インターセプターとストリーミングをサポート
#
# csv (pub points: 130/140, likes: 100+)
# - コミュニティパッケージ
# - CSV パース/書き込みを処理
# - アクティブなメンテナンス
#
# json_serializable (pub points: 140/140, likes: 500+)
# - Google パッケージ
# - JSON のコード生成
# - 型安全、AOT 対応
6. プライベートパッケージと Git 依存関係
▶ サンプル:Git 依存関係
dependencies:
# パブリック git リポジトリ
custom_client:
git:
url: https://github.com/bob/custom_client.git
ref: v1.0.0 # タグ、ブランチ、またはコミット
# プライベート git リポジトリ (SSH)
internal_sdk:
git:
url: git@github.com:bob/internal_sdk.git
ref: main
# git リポジトリ内の特定パス
shared_utils:
git:
url: https://github.com/bob/monorepo.git
path: packages/shared_utils
ref: stable
▶ サンプル:ローカルパス依存関係
# ローカル開発とテスト用
dependencies:
core_lib:
path: ../core_lib
shared_models:
path: ./packages/shared_models
| 依存関係ソース | 構文 | 適用シナリオ |
|---|---|---|
| pub.dev | package: ^1.0.0 |
正式な依存関係(推奨) |
| Git | git: url: ... |
未公開/プライベートパッケージ |
| ローカルパス | path: ../local |
開発/デバッグ、モノレポ |
7. Bob のシナリオ:DataPipeline 依存関係設定
▶ サンプル:完全なプロジェクト依存関係
name: datapipeline
description: E-commerce analytics CLI tool for processing million-level orders
version: 1.0.0
environment:
sdk: ^3.0.0
dependencies:
# CLI フレームワーク
args: ^2.4.2
# コンソール出力フォーマット
cli_util: ^0.4.1
# HTTP クライアント
http: ^1.2.0
# CSV パース
csv: ^6.0.0
# JSON シリアライゼーション
json_annotation: ^4.8.0
# パスユーティリティ
path: ^1.9.0
# ロギング
logging: ^1.2.0
# YAML 設定
yaml: ^3.1.2
dev_dependencies:
# テスト
test: ^1.24.0
# モック
mockito: ^5.4.0
# コード生成
build_runner: ^2.4.0
json_serializable: ^6.7.0
# リント
lints: ^3.0.0
# カバレッジ
coverage: ^1.6.0
8. 完全なサンプル:DataPipeline 依存関係管理
// ============================================
// DataPipeline 依存関係管理デモ
// 主要依存関係の使い方を示す
// ============================================
import 'package:args/args.dart';
import 'package:path/path.dart' as p;
const String version = '1.0.0';
class DataPipelineCli {
final ArgParser parser;
DataPipelineCli()
: parser = ArgParser()
..addFlag('version', abbr: 'v', negatable: false, help: 'Show version')
..addFlag('help', abbr: 'h', negatable: false, help: 'Show help')
..addOption('input', abbr: 'i', help: 'Input data source')
..addOption('output', abbr: 'o', defaultsTo: 'report.json', help: 'Output path')
..addOption('format', allowed: ['json', 'csv', 'html'], defaultsTo: 'json')
..addFlag('verbose', abbr: 'V', help: 'Verbose logging')
..addOption('batch-size', defaultsTo: '10000', help: 'Records per batch');
Future<void> run(List<String> arguments) async {
try {
final results = parser.parse(arguments);
if (results['help'] as bool) {
_printHelp();
return;
}
if (results['version'] as bool) {
print('DataPipeline v$version');
return;
}
final input = results['input'] as String?;
final output = results['output'] as String;
final format = results['format'] as String;
final verbose = results['verbose'] as bool;
final batchSize = int.parse(results['batch-size'] as String);
if (input == null) {
print('Error: --input is required');
_printHelp();
return;
}
// path パッケージをクロスプラットフォームパスに使用
final inputPath = p.normalize(input);
final outputPath = p.normalize(output);
final ext = p.extension(inputPath);
print('=== DataPipeline v$version ===');
if (verbose) {
print('Input: $inputPath (${ext.isEmpty ? "unknown" : ext})');
print('Output: $outputPath');
print('Format: $format');
print('Batch size: $batchSize records');
print('SDK: ${_getSdkInfo()}');
}
print('Processing: $inputPath → $outputPath ($format)');
} on FormatException catch (e) {
print('Argument error: ${e.message}');
print(parser.usage);
}
}
void _printHelp() {
print('DataPipeline - E-commerce analytics CLI tool');
print('');
print('Usage: datapipeline [options]');
print(parser.usage);
}
String _getSdkInfo() {
// 実際のプロジェクトでは dart:io Platform を使用
return 'Dart 3.x';
}
}
void main(List<String> arguments) async {
final cli = DataPipelineCli();
await cli.run(arguments);
}
> 出力: ローカルの DartPad または `dart run` で実行してください。この Dart コースの全例は Dart 3.x / Flutter 3.x ベースです。SDK バージョンにより結果が多少異なる場合があります。
出力(
dart run bin/main.dart -i orders.csv -o report.json -V):
=== DataPipeline v1.0.0 ===
Input: orders.csv (.csv)
Output: report.json
Format: json
Batch size: 10000 records
SDK: Dart 3.x
Processing: orders.csv → report.json (json)
❓ よくある質問
Q:
dependenciesとdev_dependenciesの違いは何ですか? A:dependenciesはランタイムに必要なパッケージで、dev_dependenciesは開発中(テスト、コード生成、リント)にのみ必要です。パッケージを公開する際、dev_dependenciesはユーザーに渡されません。
Q:
^と>=の違いは何ですか? A:^1.2.0は>=1.2.0 <2.0.0と同等で、メジャーバージョン内の更新に制限します。>=1.2.0には上限がありません。^の方が安全で推奨されます。
Q:
dart pub upgradeとdart pub getの違いは何ですか? A:dart pub getは pubspec.yaml の制約内で依存関係を取得します。dart pub upgradeはそれらの制約内で最新バージョンへのアップグレードを試みます。
Q: pubspec.ロック はバージョン管理にコミットすべきですか? A: アプリケーションプロジェクト(CLI、Flutter App)では、チームが同じバージョンを使用することを保証するために、はいです。ライブラリプロジェクト(パッケージ)では、ユーザーが最新の互換バージョンを取得できるように、いいえ。
Q: pub.dev でパッケージを選ぶには? A: pub points(≥130 が良好)、likes、最近の更新時刻、パブリッシャーが認証済みかを確認してください。公式 Dart/Google パッケージを優先してください。
Q: いつ
dependency_overridesを使うべきですか? A: 通常のバージョン制約では競合が解決できない場合のみ一時的に使用してください。長期使用は根本的な問題を隠します。問題が解決したらすぐに削除してください。
Q: Git 依存関係は本番環境で安全ですか? A: 推奨されません。Git 依存関係にはバージョン保証がなく、
refを強制プッシュできます。正式なリリースには pub.dev のバージョン付きパッケージを使用してください。
📖 まとめ
- pubspec.yaml はプロジェクトの設定センター:依存関係、バージョン、メタデータを管理する
- バージョン制約に
^構文を使うのが最も安全で、メジャーバージョン内での自動アップグレードが可能 - pub.dev の pub points 評価システムが質の高いパッケージ選択を助ける
- Git 依存関係は未公開/プライベートパッケージ用、パス依存関係はローカル開発用
dependenciesvsdev_dependenciesでランタイムと開発時の依存関係を分離する
📝 練習問題
- 基礎(難易度 ⭐):
dart createを使ってプロジェクトを作成し、argsとpath依存関係を追加してdart pub getを実行し、pubspec.ロック ファイルの内容を調べてください。 - 中級(難易度 ⭐⭐):pub.dev で
httpパッケージを検索し、pub points、likes、最新バージョン、サポートされるプラットフォームを記録してください。パッケージ選択評価レポートを書いてください。 - 挑戦(難易度 ⭐⭐⭐):git 依存関係とパス依存関係を含む pubspec.yaml ファイルを作成し、モノレポ開発シナリオをシミュレートしてください。
dependency_overridesを使って仮のバージョン競合を解決します。