This commit is contained in:
Tobe O 2017-10-19 21:06:15 -04:00
parent 7c27a329e5
commit bff1f6aed1
8 changed files with 306 additions and 29 deletions

View file

@ -4,21 +4,11 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.pub" /> <excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" /> <excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/bin/packages" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/.pub" /> <excludeFolder url="file://$MODULE_DIR$/sample_project/.pub" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/bin/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/build" /> <excludeFolder url="file://$MODULE_DIR$/sample_project/build" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/test/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/test/services/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/tool/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/web/images/packages" />
<excludeFolder url="file://$MODULE_DIR$/sample_project/web/packages" />
<excludeFolder url="file://$MODULE_DIR$/temp" /> <excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" /> <excludeFolder url="file://$MODULE_DIR$/tmp" />
<excludeFolder url="file://$MODULE_DIR$/tool/packages" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

View file

@ -17,6 +17,7 @@ main(List<String> args) async {
..addCommand(new KeyCommand()) ..addCommand(new KeyCommand())
..addCommand(new ServiceCommand()) ..addCommand(new ServiceCommand())
..addCommand(new InitCommand()) ..addCommand(new InitCommand())
..addCommand(new InstallCommand())
..addCommand(new TestCommand()) ..addCommand(new TestCommand())
..addCommand(new PluginCommand()) ..addCommand(new PluginCommand())
..addCommand(new StartCommand()) ..addCommand(new StartCommand())

View file

@ -4,6 +4,7 @@ export 'controller.dart';
export "doctor.dart"; export "doctor.dart";
export "key.dart"; export "key.dart";
export "init.dart"; export "init.dart";
export "install.dart";
export "make.dart"; export "make.dart";
export "plugin.dart"; export "plugin.dart";
export "rename.dart"; export "rename.dart";

View file

@ -19,7 +19,14 @@ class InitCommand extends Command {
"Initializes a new Angel project in the current directory."; "Initializes a new Angel project in the current directory.";
InitCommand() { InitCommand() {
argParser.addFlag('pub-get', defaultsTo: true); argParser
..addFlag('pub-get', defaultsTo: true)
..addFlag(
'legacy',
help:
'Generate a project using Angel 1.0.x boilerplates, rather than 1.1.x+.',
negatable: false,
);
} }
@override @override
@ -60,7 +67,7 @@ class InitCommand extends Command {
..text('\nCongratulations! You are ready to start developing with Angel!') ..text('\nCongratulations! You are ready to start developing with Angel!')
..text('\nTo start the server (with file watching), run ') ..text('\nTo start the server (with file watching), run ')
..magenta() ..magenta()
..text('`dart bin/server.dart`') ..text(argResults['legacy'] ? '`dart bin/server.dart`' : '`dart bin/dev.dart`')
..normal() ..normal()
..text(' in your terminal.') ..text(' in your terminal.')
..text('\n\nFind more documentation about Angel:') ..text('\n\nFind more documentation about Angel:')
@ -122,7 +129,9 @@ class InitCommand extends Command {
} }
print('Choose a project type before continuing:'); print('Choose a project type before continuing:');
var boilerplateChooser = new Chooser<BoilerplateInfo>(allBoilerplates); var boilerplateChooser = new Chooser<BoilerplateInfo>(
argResults['legacy'] ? legacyBoilerplates : boilerplates,
);
var boilerplate = await boilerplateChooser.choose(); var boilerplate = await boilerplateChooser.choose();
print( print(
@ -137,6 +146,17 @@ class InitCommand extends Command {
throw new Exception("Could not clone repo."); throw new Exception("Could not clone repo.");
} }
if (boilerplate.ref != null) {
git = await Process.start("git", ["checkout", boilerplate.ref]);
stdout.addStream(git.stdout);
stderr.addStream(git.stderr);
if (await git.exitCode != 0) {
throw new Exception("Could not checkout branch ${boilerplate.ref}.");
}
}
var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git")); var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git"));
if (await gitDir.exists()) await gitDir.delete(recursive: true); if (await gitDir.exists()) await gitDir.delete(recursive: true);
} catch (e) { } catch (e) {
@ -178,28 +198,43 @@ preBuild(Directory projectDir) async {
const BoilerplateInfo fullApplicationBoilerplate = const BoilerplateInfo( const BoilerplateInfo fullApplicationBoilerplate = const BoilerplateInfo(
'Full Application', 'Full Application',
'A complete project including authentication, multi-threading, and more.', 'A complete project including authentication, multi-threading, and more.',
'https://github.com/angel-dart/angel.git'); 'https://github.com/angel-dart/angel.git',
ref: '1.0.x',
);
const BoilerplateInfo lightBoilerplate = const BoilerplateInfo( const BoilerplateInfo lightBoilerplate = const BoilerplateInfo(
'Light', 'Light',
'Minimal starting point for new users.', 'Minimal starting point for new users.',
'https://github.com/angel-dart/boilerplate_light.git'); 'https://github.com/angel-dart/boilerplate_light.git',
);
const BoilerplateInfo ormBoilerplate = const BoilerplateInfo( const BoilerplateInfo ormBoilerplate = const BoilerplateInfo(
'ORM', 'ORM',
"A starting point for applications that use Angel's ORM.", "A starting point for applications that use Angel's ORM.",
'https://github.com/angel-dart/boilerplate_orm.git'); 'https://github.com/angel-dart/boilerplate_orm.git',
);
const List<BoilerplateInfo> allBoilerplates = const [ const List<BoilerplateInfo> legacyBoilerplates = const [
fullApplicationBoilerplate, fullApplicationBoilerplate,
lightBoilerplate, lightBoilerplate,
ormBoilerplate ormBoilerplate
]; ];
class BoilerplateInfo { const BoilerplateInfo basicBoilerplate = const BoilerplateInfo(
final String name, description, url; 'Basic',
'Minimal starting point - A simple server with only a few additional packages.',
'https://github.com/angel-dart/angel.git',
);
const BoilerplateInfo(this.name, this.description, this.url); const List<BoilerplateInfo> boilerplates = const [
basicBoilerplate,
ormBoilerplate,
];
class BoilerplateInfo {
final String name, description, url, ref;
const BoilerplateInfo(this.name, this.description, this.url, {this.ref});
@override @override
String toString() => '$name ($description)'; String toString() => '$name ($description)';

View file

@ -0,0 +1,245 @@
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:console/console.dart';
import 'package:glob/glob.dart';
import 'package:homedir/homedir.dart';
import 'package:mustache4dart/mustache4dart.dart' as mustache;
import 'package:path/path.dart' as p;
import 'package:pubspec/pubspec.dart';
import 'package:yaml/yaml.dart' as yaml;
import 'make/maker.dart';
class InstallCommand extends Command {
static const String repo = 'https://github.com/angel-dart/install.git';
static final Directory installRepo =
new Directory.fromUri(homeDir.uri.resolve('./.angel/addons'));
@override
String get name => 'install';
@override
String get description =>
'Installs additional add-ons to minimize boilerplate.';
InstallCommand() {
argParser
..addFlag(
'list',
help: 'List all currently-installed add-ons.',
negatable: false,
defaultsTo: false,
)
..addFlag(
'update',
help: 'Update the local add-on repository.',
negatable: false,
defaultsTo: false,
)
..addFlag(
'wipe',
help: 'Wipe the local add-on repository.',
negatable: false,
defaultsTo: false,
);
}
@override
run() async {
if (argResults['wipe']) {
if (await installRepo.exists()) await installRepo.delete();
} else if (argResults['list']) {
var addons = await list();
print('${addons.length} add-on(s) installed:');
for (var addon in addons) {
print(' * ${addon.name}@${addon.version}: ${addon.description}');
}
} else if (argResults['update']) {
await update();
} else if (argResults.rest.isNotEmpty) {
if (!await installRepo.exists())
throw 'No local add-on database exists. Run `angel install --update` first.';
var pubspec = await PubSpec.load(Directory.current);
for (var packageName in argResults.rest) {
var packageDir =
new Directory.fromUri(installRepo.uri.resolve(packageName));
if (!await packageDir.exists())
throw 'No add-on named "$packageName" is installed. You might need to run `angel install --update`.';
Map<String, dynamic> values = {
'project_name': pubspec.name,
'pubspec': pubspec,
};
List<Glob> globs = [];
var promptFile =
new File.fromUri(packageDir.uri.resolve('angel_cli.yaml'));
var projectPubspec = await PubSpec.load(packageDir);
if (await promptFile.exists()) {
var contents = await promptFile.readAsString();
var y = yaml.loadYamlDocument(contents);
var cfg = y.contents.value as Map<String, dynamic>;
// Loads globs
if (cfg['templates'] is List) {
globs.addAll(cfg['templates'].map((p) => new Glob(p)));
}
if (cfg['values'] is Map) {
var val = cfg['values'] as Map<String, dynamic>;
for (var key in val.keys) {
var desc = val[key]['description'] ?? key;
if (val[key]['type'] == 'prompt') {
Prompter prompt;
if (val[key]['default'] != null) {
prompt = new Prompter('$desc (${val[key]['default']}): ');
} else {
prompt = new Prompter('$desc: ');
}
if (val[key]['default'] != null) {
var v = await prompt.prompt();
v = v.isNotEmpty ? v : val[key]['default'];
values[key] = v;
} else
values[key] =
await prompt.prompt(checker: (s) => s.isNotEmpty);
} else if (val[key]['type'] == 'choice') {
var chooser =
new Chooser(val[key]['choices'], message: '$desc: ');
values[key] = await chooser.choose();
}
}
}
var deps = projectPubspec.dependencies.keys.map((k) {
var dep = projectPubspec.dependencies[k];
if (dep is HostedReference)
return new MakerDependency(k, dep.versionConstraint.toString());
return null;
}).where((d) => d != null);
await depend(deps);
}
Future merge(Directory src, Directory dst, String prefix) async {
if (!await src.exists()) return;
print('Copying ${src.absolute.path} into ${dst.absolute.path}...');
if (!await dst.exists()) await dst.create(recursive: true);
await for (var entity in src.list()) {
if (entity is Directory) {
var name = p.basename(entity.path);
var newDir = new Directory.fromUri(dst.uri.resolve(name));
await merge(
entity, newDir, prefix.isEmpty ? name : '$prefix/$name');
} else if (entity is File &&
!entity.path.endsWith('angel_cli.yaml')) {
var name = p.basename(entity.path);
var target = dst.uri.resolve(name);
var targetFile = new File.fromUri(target);
bool allClear = !await targetFile.exists();
if (!allClear) {
print('The file ${entity.absolute.path} already exists.');
var p = new Prompter('Overwrite the existing file? [y/N]');
var answer = await p.prompt(
checker: (s) => s.trim() == 'y' || s.trim() == 'N');
allClear = answer == 'y';
if (allClear) await targetFile.delete();
}
if (allClear) {
try {
var path = prefix.isEmpty ? name : '$prefix/$name';
if (globs.any((g) => g.matches(path))) {
print('Rendering Mustache template from ${entity.absolute
.path} to ${targetFile.absolute.path}...');
var contents = await entity.readAsString();
var renderer = mustache.compile(contents);
var generated = renderer(values);
await targetFile.writeAsString(generated);
} else {
print(
'Copying ${entity.absolute.path} to ${targetFile.absolute.path}...');
await targetFile.parent.create(recursive: true);
await entity.copy(targetFile.absolute.path);
}
} catch (_) {
print('Failed to copy.');
}
} else {
print('Skipped ${entity.absolute.path}.');
}
}
}
}
await merge(new Directory.fromUri(packageDir.uri.resolve('files')),
Directory.current, '');
print('Successfully installed $packageName@${projectPubspec.version}.');
}
} else {
print('No add-ons were specified to be installed.');
}
}
Future<List<PubSpec>> list() async {
if (!await installRepo.exists()) {
throw 'No local add-on database exists. Run `angel install --update` first.';
} else {
List<PubSpec> repos = [];
await for (var entity in installRepo.list()) {
if (entity is Directory) {
try {
repos.add(await PubSpec.load(entity));
} catch (_) {
// Ignore failures...
}
}
}
return repos;
}
}
Future update() async {
Process git;
if (!await installRepo.exists()) {
git = await Process.start('git', [
'clone',
repo,
installRepo.absolute.path,
]);
} else {
git = await Process.start(
'git',
[
'pull',
'origin',
'master',
],
workingDirectory: installRepo.absolute.path,
);
}
git..stdout.listen(stdout.add)..stderr.listen(stderr.add);
var code = await git.exitCode;
if (code != 0) {
throw 'git exited with code $code.';
}
}
}

View file

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:http/src/base_client.dart' as http; import 'package:http/src/base_client.dart' as http;
import 'package:pub_semver/pub_semver.dart'; import 'package:pub_semver/pub_semver.dart';
final Version PACKAGE_VERSION = new Version(1, 1, 5, build: '2'); final Version PACKAGE_VERSION = new Version(1, 2, 0);
Future<Version> fetchCurrentVersion(http.BaseClient client) async { Future<Version> fetchCurrentVersion(http.BaseClient client) async {
var response = var response =
await client.get('https://pub.dartlang.org/api/packages/angel_cli'); await client.get('https://pub.dartlang.org/api/packages/angel_cli');

View file

@ -21,7 +21,10 @@ class MongoServiceGenerator extends ServiceGenerator {
@override @override
void applyToLibrary(LibraryBuilder library, String name, String lower) { void applyToLibrary(LibraryBuilder library, String name, String lower) {
library.addMember(new ImportBuilder('package:mongo_dart/mongo_dart.dart')); library.addMembers([
new ImportBuilder('package:angel_mongo/angel_mongo.dart'),
new ImportBuilder('package:mongo_dart/mongo_dart.dart'),
]);
} }
@override @override

View file

@ -2,7 +2,7 @@ author: "Tobe O <thosakwe@gmail.com>"
description: "Command-line tools for the Angel framework." description: "Command-line tools for the Angel framework."
homepage: "https://github.com/angel-dart/angel_cli" homepage: "https://github.com/angel-dart/angel_cli"
name: "angel_cli" name: "angel_cli"
version: 1.1.5+2 version: 1.2.0
dependencies: dependencies:
# analyzer: "^0.29.0" # analyzer: "^0.29.0"
args: ^0.13.4 args: ^0.13.4
@ -10,9 +10,11 @@ dependencies:
console: "^2.2.3" console: "^2.2.3"
dart_style: ^1.0.0 dart_style: ^1.0.0
glob: "^1.1.0" glob: "^1.1.0"
homedir: ^0.0.4
http: ^0.11.3 http: ^0.11.3
id: "^1.0.0" id: "^1.0.0"
inflection: "^0.4.1" inflection: "^0.4.1"
mustache4dart: ^2.1.0
pubspec: "^0.0.14" pubspec: "^0.0.14"
random_string: "^0.0.1" random_string: "^0.0.1"
recase: "^1.0.0" recase: "^1.0.0"