run method

  1. @override
Future<int> run()
override

Runs this command.

The return value is wrapped in a Future if necessary and returned by CommandRunner.runCommand.

Implementation

@override
Future<int> run() async {
  final rootPath = argResults?['root'] as String? ?? fs.currentDirectory.path;
  final root = fs.directory(rootPath);

  if (!root.existsSync()) {
    logger.err('No such directory: $rootPath');

    return 1;
  }

  final basePort = int.tryParse(argResults?['base-port'] as String? ?? '');
  if (basePort == null) {
    logger.err('--base-port must be a number');

    return 1;
  }

  final services = const ServiceDiscovery().find(root);

  if (services.isEmpty) {
    logger.err(
      'No Revali services found under $rootPath.\n'
      'A service is a package with a routes/ directory that depends on '
      'revali_router.',
    );

    return 1;
  }

  final content = composeFile(services, basePort: basePort);

  if (argResults?['stdout'] as bool? ?? false) {
    io.stdout.write(content);

    return 0;
  }

  final outputPath =
      argResults?['output'] as String? ??
      p.join(root.path, 'docker-compose.yaml');
  final output = fs.file(outputPath);

  output.parent.createSync(recursive: true);
  output.writeAsStringSync(content);

  logger.success('Wrote ${services.length} service(s) to $outputPath');

  final missing = services.where((s) => !s.hasDockerfile).toList();
  if (missing.isNotEmpty) {
    // Worth saying out loud: `docker compose up` would fail on these, and
    // the reason is in a comment in a generated file nobody reads.
    final paths = missing.map((s) => s.relativePath).join(', ');

    logger.warn(
      '${missing.length} service(s) have no Dockerfile yet. '
      'Run `revali build` in: $paths',
    );
  }

  return 0;
}