exec method

  1. @override
Future<void> exec(
  1. ExecContext context
)
override

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);
}