run method

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

Runs this command.

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

Implementation

@override
Future<void> run() async {
  final args = argResults?.rest ?? [];

  if (args.isNotEmpty) {
    final subcommand = args.first.toLowerCase();

    if (subcommand == 'start') {
      final result = await Process.run('podman', [
        'machine',
        'start',
        'podman-machine-default',
      ]);
      stdout.write(result.stdout);
      stderr.write(result.stderr);
      exit(result.exitCode);
    } else if (subcommand == 'stop') {
      final result = await Process.run('podman', [
        'machine',
        'stop',
        'podman-machine-default',
      ]);
      stdout.write(result.stdout);
      stderr.write(result.stderr);
      exit(result.exitCode);
    } else if (subcommand == 'reset') {
      // Check if podman-machine-default exists
      final lsResult = await Process.run('podman', [
        'machine',
        'list',
        '--format',
        '{{.Name}}',
      ]);
      if (lsResult.exitCode != 0) {
        stderr.writeln('Error listing Podman machines: ${lsResult.stderr}');
        exit(lsResult.exitCode);
      }

      final machines = (lsResult.stdout as String).trim().split('\n');
      final machineExists = machines.contains('podman-machine-default');

      if (machineExists) {
        final steps = [
          ['podman', 'machine', 'stop', 'podman-machine-default'],
          ['podman', 'machine', 'rm', '-f', 'podman-machine-default'],
          [
            'podman',
            'machine',
            'init',
            '--cpus',
            '4',
            '--memory',
            '4096',
            '--disk-size',
            '20',
          ],
          ['podman', 'machine', 'start', 'podman-machine-default'],
          ['podman', 'ps'],
        ];

        for (final step in steps) {
          stdout.writeln('Running command: ${step.join(' ')}');
          final result = await Process.run(step[0], step.sublist(1));
          stdout.write(result.stdout);
          stderr.write(result.stderr);
          if (result.exitCode != 0) {
            stderr.writeln(
              'Error: command "${step.join(' ')}" failed with exit code ${result.exitCode}.',
            );
            exit(result.exitCode);
          }
        }
      } else {
        stdout.writeln(
          'Podman machine "podman-machine-default" does not exist. Initializing and starting a new machine.',
        );
        final initResult = await Process.run('podman', [
          'machine',
          'init',
          '--cpus',
          '4',
          '--memory',
          '4096',
          '--disk-size',
          '20',
        ]);
        stdout.write(initResult.stdout);
        stderr.write(initResult.stderr);
        if (initResult.exitCode != 0) {
          stderr.writeln(
            'Error: command "podman machine init" failed with exit code ${initResult.exitCode}.',
          );
          exit(initResult.exitCode);
        }

        final startResult = await Process.run('podman', [
          'machine',
          'start',
          'podman-machine-default',
        ]);
        stdout.write(startResult.stdout);
        stderr.write(startResult.stderr);
        if (startResult.exitCode != 0) {
          stderr.writeln(
            'Error: command "podman machine start" failed with exit code ${startResult.exitCode}.',
          );
          exit(startResult.exitCode);
        }

        final psResult = await Process.run('podman', ['ps']);
        stdout.write(psResult.stdout);
        stderr.write(psResult.stderr);
        if (psResult.exitCode != 0) {
          stderr.writeln(
            'Error: command "podman ps" failed with exit code ${psResult.exitCode}.',
          );
          exit(psResult.exitCode);
        }
      }
      exit(0);
    } else if (subcommand == 'bind' &&
        args.length > 1 &&
        args[1].toLowerCase() == 'mount') {
      String? image = argResults?['image'];

      if (image == null || image.trim().isEmpty) {
        stdout.write('Enter the Podman image name to use: ');
        image = stdin.readLineSync();
        if (image == null || image.trim().isEmpty) {
          stderr.writeln('Error: No image name provided.');
          exit(1);
        }
      }

      // Stop and remove any existing containers using this image
      await Process.run('podman', ['rm', '-f', image]);

      final currentDir = Directory.current.path;

      final process = await Process.start('podman', [
        'run',
        '--rm',
        '--name',
        image,
        '-p',
        '10000:10000',
        '--volume',
        '$currentDir:/app:Z',
        image,
      ], mode: ProcessStartMode.inheritStdio);
      await process.exitCode;
    } else if (subcommand == 'ps') {
      final result = await Process.run('podman', ['ps']);
      stdout.write(result.stdout);
      stderr.write(result.stderr);
      exit(result.exitCode);
    } else if (subcommand == 'exec') {
      // Collect flags and container ID
      final execArgs = args.sublist(1);
      List<String> flags = [];
      String? containerId;

      for (final arg in execArgs) {
        if (arg == '-i' || arg == '-t' || arg == '-it' || arg == '-ti') {
          flags.addAll(arg.split(''));
        } else if (!arg.startsWith('-')) {
          containerId = arg;
          break;
        }
      }

      if (containerId == null || containerId.trim().isEmpty) {
        stdout.write('Enter the container ID to exec into: ');
        containerId = stdin.readLineSync();
        if (containerId == null || containerId.trim().isEmpty) {
          stderr.writeln('Error: No container ID provided.');
          exit(1);
        }
      }

      // Ensure '-i' and '-t' flags are included
      if (!flags.contains('i')) flags.add('i');
      if (!flags.contains('t')) flags.add('t');

      // Sort flags to '-it' order
      flags.sort();

      final flagString = '-${flags.join('')}';

      final process = await Process.start('podman', [
        'exec',
        flagString,
        containerId,
        '/bin/bash',
      ], mode: ProcessStartMode.inheritStdio);
      final exitCode = await process.exitCode;
      exit(exitCode);
    }
  }

  final showLogs = argResults?['logs'] == true;

  if (showLogs) {
    // Detect the last built image
    final result = await Process.run('podman', [
      'images',
      '--format',
      '{{.Repository}} {{.CreatedAt}}',
    ]);
    if (result.exitCode != 0) {
      stderr.writeln('Error listing images: ${result.stderr}');
      exit(1);
    }

    final lines = (result.stdout as String).trim().split('\n');
    if (lines.isEmpty) {
      stderr.writeln('No Podman images found.');
      exit(1);
    }

    // Sort by creation date, assume newest first
    lines.sort((a, b) {
      final dateA =
          DateTime.tryParse(a.split(' ').sublist(1).join(' ')) ??
          DateTime(1970);
      final dateB =
          DateTime.tryParse(b.split(' ').sublist(1).join(' ')) ??
          DateTime(1970);
      return dateB.compareTo(dateA);
    });

    final lastImage = lines.first.split(' ').first;

    // Get container ID for the last image
    final psResult = await Process.run('podman', [
      'ps',
      '-a',
      '--filter',
      'ancestor=$lastImage',
      '--format',
      '{{.ID}}',
    ]);
    if (psResult.exitCode != 0 ||
        (psResult.stdout as String).trim().isEmpty) {
      stderr.writeln('No container found for image $lastImage.');
      exit(1);
    }

    final containerId = (psResult.stdout as String).trim().split('\n').first;

    // Show logs
    final logProcess = await Process.start('podman', [
      'logs',
      '-f',
      containerId,
    ]);
    stdout.addStream(logProcess.stdout);
    stderr.addStream(logProcess.stderr);
    await logProcess.exitCode;
    return;
  }

  String? image = argResults?['image'];

  if (image == null || image.trim().isEmpty) {
    stdout.write('Enter the Podman image name to use: ');
    image = stdin.readLineSync();
    if (image == null || image.trim().isEmpty) {
      stderr.writeln('Error: No image name provided.');
      exit(1);
    }
  }

  final steps = <List<String>>[];

  // Default behavior: always rebuild and run the container.
  // The separate 'bind mount' subcommand above handles bind-mount runs.
  steps.add([
    'podman',
    'rm',
    '-f',
    r'$(podman ps -aq)',
  ]); // Remove all containers
  steps.add(['podman', 'rmi', '-f', image]); // Remove existing image
  steps.add(['podman', 'build', '-t', image, '.']); // Build image

  final currentDir = Directory.current.path;

  steps.add([
    'podman',
    'run',
    '-d',
    '-p',
    '10000:10000',
    '--volume',
    '$currentDir:/app:Z',
    image,
  ]);

  for (final step in steps) {
    final process = await Process.start(step[0], step.sublist(1));
    stdout.addStream(process.stdout);
    stderr.addStream(process.stderr);
    final exitCode = await process.exitCode;
    if (exitCode != 0) {
      stderr.writeln(
        'Error: command "${step.join(' ')}" failed with exit code $exitCode.',
      );
      exit(exitCode);
    }
  }
}