buildAndroid function

Future<void> buildAndroid(
  1. ExecContext context, {
  2. required String gh,
  3. required String appName,
  4. required int defaultIncrementNumber,
})

Gibhut Actions build for Android.

Android用のGibhut Actionsのビルド。

Implementation

Future<void> buildAndroid(
  ExecContext context, {
  required String gh,
  required String appName,
  required int defaultIncrementNumber,
}) async {
  final github = context.yaml.getAsMap("github");
  final action = github.getAsMap("action");
  final android = action.getAsMap("android");
  final changesNotSentForReview =
      android.get("changes_not_sent_for_review", false);
  final status = android.get("status", "draft");
  final keystoreFile = File("android/app/appkey.keystore");
  final secretGithub = context.secrets.getAsMap("github");
  final slack = secretGithub.getAsMap("slack");
  final slackIncomingWebhookUrl = slack.get("incoming_webhook_url", "");
  if (!keystoreFile.existsSync()) {
    error(
      "Cannot find `android/app/appkey.keystore`. Run `katana app keystore` to create the keystore file. `android/app/appkey.keystore`が見つかりません。`katana app keystore`を実行しキーストアのファイルを作成してください。",
    );
    return;
  }
  final keyPropertiesFile = File("android/key.properties");
  if (!keyPropertiesFile.existsSync()) {
    error(
      "Cannot find `android/key.properties`. Run `katana app keystore` to create the keystore file. `android/key.properties`が見つかりません。`katana app keystore`を実行しキーストアのファイルを作成してください。",
    );
    return;
  }
  final serviceAccountFile = await find(
    Directory("android"),
    RegExp("([a-z0-9_-]+).json"),
    recursive: false,
  );
  if (serviceAccountFile == null) {
    error(
      "Json for service account not found, please refer to https://mathru.notion.site/Google-Play-Developer-df655aff2dfb49988b82feb7aae3c61b to set it up. サービスアカウント用のJsonが見つかりません。https://mathru.notion.site/Google-Play-Developer-df655aff2dfb49988b82feb7aae3c61b を参考に設定してください。",
    );
    return;
  }
  final gradle = AppGradle();
  await gradle.load();
  final packageName = gradle.android?.defaultConfig.applicationId;
  if (packageName == null) {
    error(
      "Cannot find [android]->[defaultConfig]->[applicationId] in `android/app/build.gradle`.",
    );
    return;
  }
  final keystore = base64.encode(await keystoreFile.readAsBytes());
  final keyProperties = base64.encode(await keyPropertiesFile.readAsBytes());
  final serviceAccount = base64.encode(await serviceAccountFile.readAsBytes());
  await command(
    "Store `appkey.keystore` in `secrets.ANDROID_KEYSTORE_${appName.toUpperCase()}`.",
    [
      gh,
      "secret",
      "set",
      "ANDROID_KEYSTORE_${appName.toUpperCase()}",
      "--body",
      keystore,
    ],
  );
  await command(
    "Store `key.properties` in `secrets.ANDROID_KEY_PROPERTIES_${appName.toUpperCase()}`.",
    [
      gh,
      "secret",
      "set",
      "ANDROID_KEY_PROPERTIES_${appName.toUpperCase()}",
      "--body",
      keyProperties,
    ],
  );
  await command(
    "Store `service_account.json` in `secrets.ANDROID_SERVICE_ACCOUNT_KEY_JSON_${appName.toUpperCase()}`.",
    [
      gh,
      "secret",
      "set",
      "ANDROID_SERVICE_ACCOUNT_KEY_JSON_${appName.toUpperCase()}",
      "--body",
      serviceAccount,
    ],
  );
  final gitDir = await findGitDirectory(Directory.current);
  final androidCode = GithubActionsAndroidCliCode(
    workingDirectory: gitDir,
    defaultIncrementNumber: defaultIncrementNumber,
    changesNotSentForReview: changesNotSentForReview,
    status: status,
  );
  await androidCode.generateFile(
    "build_android_${appName.toLowerCase()}.yaml",
    filter: (value) {
      return androidCode._additionalSlackFilter(
        value
            .replaceAll(
              "#### REPLACE_ANDROID_PACKAGE_NAME ####",
              packageName.replaceAll('"', ""),
            )
            .replaceAll(
              "#### REPLACE_APP_NAME ####",
              appName.toUpperCase(),
            ),
        slackIncomingWebhookUrl,
      );
    },
  );
  label("Rewrite `.gitignore`.");
  final gitignore = File("android/.gitignore");
  if (!gitignore.existsSync()) {
    error("Cannot find `android/.gitignore`. Project is broken.");
    return;
  }
  final gitignores = await gitignore.readAsLines();
  if (context.yaml.getAsMap("git").get("ignore_secure_file", true)) {
    if (!gitignores.any((e) => e.startsWith("key.properties"))) {
      gitignores.add("key.properties");
    }
    if (!gitignores.any((e) => e.startsWith("**/*.keystore"))) {
      gitignores.add("**/*.keystore");
    }
    if (!gitignores.any((e) => e.startsWith("**/*.jks"))) {
      gitignores.add("**/*.jks");
    }
    if (!gitignores.any((e) => e.startsWith("**/*.p12"))) {
      gitignores.add("**/*.p12");
    }
    if (!gitignores.any((e) => e.startsWith("*.json"))) {
      gitignores.add("*.json");
    }
    if (!gitignores.any((e) => e.startsWith("**/appkey_fingerprint.txt"))) {
      gitignores.add("**/appkey_fingerprint.txt");
    }
    if (!gitignores.any((e) => e.startsWith("**/appkey_password.key"))) {
      gitignores.add("**/appkey_password.key");
    }
  } else {
    gitignores.removeWhere((e) => e.startsWith("**/*.p12"));
    gitignores.removeWhere((e) => e.startsWith("*.json"));
    gitignores.removeWhere((e) => e.startsWith("**/appkey_fingerprint.txt"));
    gitignores.removeWhere((e) => e.startsWith("**/appkey_password.key"));
    gitignores.removeWhere((e) => e.startsWith("key.properties"));
    gitignores.removeWhere((e) => e.startsWith("**/*.keystore"));
    gitignores.removeWhere((e) => e.startsWith("**/*.jks"));
  }
  await gitignore.writeAsString(gitignores.join("\n"));
}