Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Init command #780

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/melos/lib/src/command_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'command_runner/bootstrap.dart';
import 'command_runner/clean.dart';
import 'command_runner/exec.dart';
import 'command_runner/format.dart';
import 'command_runner/init.dart';
import 'command_runner/list.dart';
import 'command_runner/publish.dart';
import 'command_runner/run.dart';
Expand All @@ -38,7 +39,8 @@ class MelosCommandRunner extends CommandRunner<void> {
: super(
'melos',
'A CLI tool for managing Dart & Flutter projects with multiple '
'packages.',
'packages.\n\n'
'To get started with Melos, run "melos init".',
usageLineLength: terminalWidth,
) {
argParser.addFlag(
Expand All @@ -55,6 +57,7 @@ class MelosCommandRunner extends CommandRunner<void> {
'the special value "auto".',
);

addCommand(InitCommand(config));
addCommand(ExecCommand(config));
addCommand(BootstrapCommand(config));
addCommand(CleanCommand(config));
Expand Down Expand Up @@ -154,6 +157,7 @@ Future<MelosWorkspaceConfig> _resolveConfig(
}

bool _shouldUseEmptyConfig(List<String> arguments) {
if (arguments.firstOrNull == 'init') return true;
final willShowHelp = arguments.isEmpty ||
arguments.contains('--help') ||
arguments.contains('-h');
Expand Down
71 changes: 71 additions & 0 deletions packages/melos/lib/src/command_runner/init.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'dart:io';

import 'package:path/path.dart' as p;

import '../../melos.dart';
import '../common/utils.dart';
import 'base.dart';

class InitCommand extends MelosCommand {
InitCommand(super.config) {
argParser.addOption(
'directory',
abbr: 'd',
help: 'Directory to create project in. Defaults to the workspace name.',
);

argParser.addMultiOption(
'packages',
abbr: 'p',
help: 'Comma separated glob paths to add to the melos workspace.',
);

argParser.addOption(
'project',
abbr: 'P',
help: 'Project name to be used in pubspec.yaml. '
'Defaults to the workspace name.',
);
}

@override
final String name = 'init';

@override
final String description = 'Initialize a new Melos workspace.';

@override
Future<void> run() {
final workspaceDefault = p.basename(Directory.current.absolute.path);
final workspaceName = argResults!.rest.firstOrNull ??
promptInput(
'Enter your workspace name',
defaultsTo: workspaceDefault,
);
final directory = argResults!['directory'] as String? ??
promptInput(
'Enter the directory',
defaultsTo: workspaceDefault != workspaceName ? workspaceName : '.',
);
final packages = argResults!['packages'] as List<String>?;
final project = argResults!['project'] as String? ??
promptInput(
'Enter the project name',
defaultsTo: workspaceName,
);
Comment on lines +51 to +55
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that we need to differentiate between workspace name and project name

final useAppsDir = promptBool(
message: 'Do you want to add the apps directory to the list of packages?',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
message: 'Do you want to add the apps directory to the list of packages?',
message: 'Do you want to add the apps directory?',

We are actually creating the apps directory right? I don't think we need to specify that it will be added in the melos.yaml file too, and the wording as is is slightly confusing.

defaultsTo: true,
);

final melos = Melos(logger: logger, config: config);

return melos.init(
workspaceName,
directory: directory,
packages: packages ?? const [],
project: project,
useAppDir: useAppsDir,
);
}
}
63 changes: 63 additions & 0 deletions packages/melos/lib/src/commands/init.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
part of 'runner.dart';

mixin _InitMixin on _Melos {
Future<void> init(
String workspaceName, {
required String directory,
required List<String> packages,
required String project,
bool useAppDir = false,
}) async {
late final String qualifiedWorkspaceName;
if (workspaceName == '.') {
qualifiedWorkspaceName = p.basename(Directory.current.absolute.path);
} else {
qualifiedWorkspaceName = workspaceName;
}

final isCurrentDir = directory == '.';
final dir = Directory(directory);
if (!isCurrentDir && dir.existsSync()) {
throw StateError('Directory $directory already exists');
} else if (!isCurrentDir) {
dir.createSync(recursive: true);
Directory(p.join(dir.absolute.path, 'packages')).createSync();
if (useAppDir) {
Directory(p.join(dir.absolute.path, 'apps')).createSync();
}
}

final dartVersion = utils.currentDartVersion('dart');
final melosYaml = <String, Object?>{
'name': qualifiedWorkspaceName,
'packages': [if (useAppDir) 'apps/*', 'packages/*'],
if (packages.isNotEmpty) 'packages': packages,
};
final pubspecYaml = <String, dynamic>{
exaby73 marked this conversation as resolved.
Show resolved Hide resolved
'name': project,
'environment': {
'sdk': '>=$dartVersion <${dartVersion.major + 1}.0.0',
},
'dev_dependencies': {
'melos': '^$melosVersion',
},
};

final melosFile = File(p.join(dir.absolute.path, 'melos.yaml'));
final pubspecFile = File(p.join(dir.absolute.path, 'pubspec.yaml'));

melosFile.writeAsStringSync(
(YamlEditor('')..update([], melosYaml)).toString(),
);
pubspecFile.writeAsStringSync(
(YamlEditor('')..update([], pubspecYaml)).toString(),
);

logger.log(
'Initialized Melos workspace in ${dir.path}.\n'
'Run the following commands to bootstrap the workspace:\n'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'Run the following commands to bootstrap the workspace:\n'
'Run the following commands to bootstrap the workspace when you have created some packages and/or apps:\n'

Since this won't do anything when run directly after init has been run we should explain that they should run this once the apps and/or packages are created.

' cd ${dir.path}\n'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the path is . we should omit this

' melos bootstrap',
);
}
}
6 changes: 5 additions & 1 deletion packages/melos/lib/src/commands/runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:pubspec/pubspec.dart';
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';

import '../../version.g.dart';
spydon marked this conversation as resolved.
Show resolved Hide resolved
import '../command_configs/command_configs.dart';
import '../command_runner/version.dart';
import '../common/aggregate_changelog.dart';
Expand Down Expand Up @@ -52,6 +53,7 @@ part 'run.dart';
part 'version.dart';
part 'analyze.dart';
part 'format.dart';
part 'init.dart';

enum CommandWithLifecycle {
bootstrap,
Expand All @@ -70,7 +72,8 @@ class Melos extends _Melos
_VersionMixin,
_PublishMixin,
_AnalyzeMixin,
_FormatMixin {
_FormatMixin,
_InitMixin {
Melos({
required this.config,
Logger? logger,
Expand All @@ -84,6 +87,7 @@ class Melos extends _Melos

abstract class _Melos {
MelosLogger get logger;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

MelosWorkspaceConfig get config;

Future<MelosWorkspace> createWorkspace({
Expand Down
Loading