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 arguments = context.args.skip(1).toList();
if (arguments.isEmpty ||
!{"status", "diff", "generate", "apply", "mark"}
.contains(arguments.first)) {
error(example);
return;
}
final command = arguments.first;
String? version;
var apply = false;
for (var i = 1; i < arguments.length; i++) {
final argument = arguments[i];
if (argument == "--apply" && !apply) {
apply = true;
} else if (argument == "--version" &&
version == null &&
i + 1 < arguments.length) {
version = arguments[++i];
} else if (argument.startsWith("--version=") && version == null) {
version = argument.substring("--version=".length);
} else if (argument == "--flavor" && i + 1 < arguments.length) {
i++;
} else if (argument.startsWith("--flavor=")) {
continue;
} else {
error("不正または重複したmigration引数です。");
return;
}
}
if (context.flavorContext?.explicit != true) {
error("migrationには--flavorの明示が必要です。");
return;
}
final config = context.yaml.getAsMap("cloudflare").getAsMap("tidb");
if (config.isEmpty) {
error(
"katana.yamlの[cloudflare]->[tidb]が見つかりません。プロジェクトルートでkatana migrateを実行してください。");
return;
}
final secrets = context.secrets.getAsMap("cloudflare").getAsMap("tidb");
final environment = context.flavorContext!.flavor.name;
final rawHost = (config["host"] ?? "");
final host = rawHost is Map
? rawHost[environment]?.toString() ?? ""
: rawHost.toString();
final rawCluster = (config["cluster_id"] ?? "");
final cluster = rawCluster is Map
? rawCluster[environment]?.toString() ?? ""
: rawCluster.toString();
final rawDatabase = (config["database"] ?? "");
final database = rawDatabase is Map
? rawDatabase[environment]?.toString() ?? ""
: rawDatabase.toString();
final credentials = await resolveTidbMigrationCredentials(
environment: environment,
cluster: cluster,
host: host,
database: database,
node: context.yaml.getAsMap("bin").get("node", "node").toString(),
legacySecrets: Map<String, dynamic>.from(secrets),
publicKey: Platform.environment["TIDBCLOUD_PUBLIC_KEY"] ??
_tidbApiKeyValue(
Map<String, dynamic>.from(secrets), "public_key", environment),
privateKey: Platform.environment["TIDBCLOUD_PRIVATE_KEY"] ??
_tidbApiKeyValue(
Map<String, dynamic>.from(secrets), "private_key", environment),
authMode: _tidbEnvironmentSetting(config, "migration_auth", environment,
fallback: "api_key"),
oauthProfile: _tidbEnvironmentSetting(
config, "migration_auth_profile", environment,
fallback: "default"),
allowProvision: false,
);
final input = {
"command": command,
"root": Directory.current.path,
"schemaPath": config.get("schema", "tidb/schema/schema.json"),
"directory": config.get("migrations", "tidb/migrations"),
"target": {
"environment": environment,
"cluster": cluster,
"host": host,
"database": database,
"principal": credentials.username,
},
"version": version,
"apply": apply,
"username": credentials.username,
"password": credentials.password,
};
// node_modulesの既存packageを使い、暗黙のinstallやnpx取得をしない。
final node = context.yaml.getAsMap("bin").get("node", "node");
const script =
'require(require.resolve("@mathrunet/masamune_cloudflare_tidb/dist/migrate.js"))';
// requireだけではmainが起動しないためrunMigrateを明示呼び出しする。
final process = await Process.start(
node,
[
"-e",
'''
const migration = $script;
let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", chunk => { input += chunk; });
process.stdin.on("end", async () => {
try { console.log(JSON.stringify(await migration.runMigrate(JSON.parse(input)))); }
catch (error) { console.error(error.message); process.exitCode = 1; }
});
'''
],
workingDirectory: "cloudflare");
final output = process.stdout.transform(utf8.decoder).join();
final failure = process.stderr.transform(utf8.decoder).join();
process.stdin.write(jsonEncode(input));
await process.stdin.close();
final status = await process.exitCode;
final outputText = await output;
final failureText = await failure;
if (status != 0) {
error(failureText.trim());
return;
}
stdout.write(outputText);
}