1.2.0
This commit is contained in:
parent
7c27a329e5
commit
bff1f6aed1
8 changed files with 306 additions and 29 deletions
|
@ -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" />
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -176,30 +196,45 @@ 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)';
|
||||||
|
|
245
lib/src/commands/install.dart
Normal file
245
lib/src/commands/install.dart
Normal 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.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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');
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue