exec method
Run command.
The contents of katana.yaml and the arguments of the command are passed to context.
コマンドを実行します。
contextにkatana.yamlの内容やコマンドの引数が渡されます。
Implementation
@override
Future<void> exec(ExecContext context) async {
final bin = context.yaml.getAsMap("bin");
final npm = bin.get("npm", "npm");
final wrangler = bin.get("wrangler", "wrangler");
final cloudflare = context.yaml.getAsMap("cloudflare");
final storage = cloudflare.getAsMap("storage");
final binding = storage.get("binding", "R2_BUCKET");
final bucketName = storage.get("bucket_name", "");
final previewBucketName = storage.get("preview_bucket_name", "");
final publicBaseUrl = storage.get("public_base_url", "");
final configuredDownloadUrlSecret = storage.get("download_url_secret", "");
final backup = storage.getAsMap("backup");
final backupEnabled = backup.get("enable", false);
final backupBinding = backup.get("binding", "R2_BACKUP_BUCKET");
final backupBucketName = backup.get("bucket_name", "");
final backupPreviewBucketName = backup.get("preview_bucket_name", "");
final backupQueueName = backup.get(
"queue_name",
bucketName.isEmpty ? "" : "$bucketName-backup",
);
final backupMaxBatchSize = backup.get("max_batch_size", 10);
final backupMaxBatchTimeout = backup.get("max_batch_timeout", 5);
final backupMaxRetries = backup.get("max_retries", 3);
final backupDeadLetterQueue = backup.get(
"dead_letter_queue",
backupQueueName.isEmpty ? "" : "$backupQueueName-dlq",
);
if (binding.isEmpty) {
error(
"If [cloudflare]->[storage]->[enable] is enabled, please include [cloudflare]->[storage]->[binding].",
);
return;
}
if (bucketName.isEmpty) {
error(
"If [cloudflare]->[storage]->[enable] is enabled, please include [cloudflare]->[storage]->[bucket_name]. Create it with `wrangler r2 bucket create <bucket_name>`.",
);
return;
}
if (publicBaseUrl.isEmpty) {
error(
"If [cloudflare]->[storage]->[enable] is enabled, please include [cloudflare]->[storage]->[public_base_url].",
);
return;
}
if (backupEnabled) {
if (backupBinding.isEmpty) {
error(
"If [cloudflare]->[storage]->[backup]->[enable] is enabled, please include [binding].",
);
return;
}
if (backupBucketName.isEmpty) {
error(
"If [cloudflare]->[storage]->[backup]->[enable] is enabled, please include [bucket_name]. Create it with `wrangler r2 bucket create <bucket_name>`.",
);
return;
}
if (backupQueueName.isEmpty) {
error(
"If [cloudflare]->[storage]->[backup]->[enable] is enabled, please include [queue_name].",
);
return;
}
if (binding == backupBinding || bucketName == backupBucketName) {
error(
"Cloudflare R2 backup must use a different binding and bucket from the source storage.",
);
return;
}
if (backupMaxBatchSize <= 0 ||
backupMaxBatchTimeout < 0 ||
backupMaxRetries < 0) {
error(
"Cloudflare R2 backup queue settings must be non-negative, and [max_batch_size] must be greater than zero.",
);
return;
}
}
final downloadUrlSecret =
await _loadOrCreateDownloadUrlSecret(configuredDownloadUrlSecret);
final cloudflareDir = Directory("cloudflare");
if (!cloudflareDir.existsSync()) {
error(
"The directory `cloudflare` does not exist. Initialize Cloudflare Workers by enabling [cloudflare]->[workers]->[enable] and executing `katana apply`.",
);
return;
}
final indexFile = File("cloudflare/src/index.ts");
if (!indexFile.existsSync()) {
error(
"The file `cloudflare/src/index.ts` does not exist. Initialize Cloudflare Workers by enabling [cloudflare]->[workers]->[enable] and executing `katana apply`.",
);
return;
}
final wranglerFile = File("cloudflare/wrangler.jsonc");
if (!wranglerFile.existsSync()) {
error(
"The file `cloudflare/wrangler.jsonc` does not exist. Initialize Cloudflare Workers by enabling [cloudflare]->[workers]->[enable] and executing `katana apply`.",
);
return;
}
label("Add Cloudflare Workers functions");
final source = await indexFile.readAsString();
final updated = _updateStorageFunctions(
source,
binding: binding,
bucketName: bucketName,
publicBaseUrl: publicBaseUrl,
backupEnabled: backupEnabled,
backupBinding: backupBinding,
);
if (updated == null) {
return;
}
await indexFile.writeAsString(updated);
label("Add Cloudflare R2 bucket binding");
var wranglerSource = _updateWranglerR2Bucket(
await wranglerFile.readAsString(),
binding: binding,
bucketName: bucketName,
previewBucketName: previewBucketName,
backupEnabled: backupEnabled,
backupBinding: backupBinding,
backupBucketName: backupBucketName,
backupPreviewBucketName: backupPreviewBucketName,
);
if (backupEnabled) {
wranglerSource = _updateWranglerQueueConsumer(
wranglerSource,
queueName: backupQueueName,
maxBatchSize: backupMaxBatchSize,
maxBatchTimeout: backupMaxBatchTimeout,
maxRetries: backupMaxRetries,
deadLetterQueue: backupDeadLetterQueue,
);
}
await wranglerFile.writeAsString(wranglerSource);
await command(
"Package installation.",
[
npm,
"install",
"@mathrunet/masamune_cloudflare_storage",
],
workingDirectory: "cloudflare",
runInShell: true,
);
await addFlutterImport(
[
"masamune_storage_cloudflare",
],
);
await _putWranglerSecret(
wrangler: wrangler,
key: "STORAGE_DOWNLOAD_URL_SECRET",
value: downloadUrlSecret,
);
if (backupEnabled) {
await _ensureQueue(wrangler: wrangler, queueName: backupQueueName);
if (backupDeadLetterQueue.isNotEmpty) {
await _ensureQueue(
wrangler: wrangler,
queueName: backupDeadLetterQueue,
);
}
await _ensureR2CreateNotification(
wrangler: wrangler,
bucketName: bucketName,
queueName: backupQueueName,
);
}
}