diff --git a/bin/angel.dart b/bin/angel.dart index f8919c4c..8288adf6 100644 --- a/bin/angel.dart +++ b/bin/angel.dart @@ -4,10 +4,13 @@ library demon.tool; import "dart:io"; import "package:args/command_runner.dart"; import 'package:angel_cli/angel_cli.dart'; +import 'package:angel_cli/pubspec.update.g.dart'; +import 'package:console/console.dart'; +import 'package:http/http.dart' as http; final String DOCTOR = "doctor"; -main(List args) { +main(List args) async { var runner = new CommandRunner("angel", "Command-line tools for the Angel framework."); @@ -21,7 +24,24 @@ main(List args) { ..addCommand(new StartCommand()) ..addCommand(new RenameCommand()); - return runner.run(args).then((_) {}).catchError((exc) { + stdout.write('Checking for update... '); + var client = new http.Client(); + var update = await checkForUpdate(client); + client.close(); + + if (update != null) { + stdout.writeln(); + var pen = new TextPen(); + pen.cyan(); + pen.text( + 'ATTENTION: There is a new version of the Angel CLI available (version $update).'); + pen.text('\nTo update, run `pub global activate angel_cli`.'); + pen(); + stdout.writeln(); + } else + stdout.writeln('No update available.'); + + return await runner.run(args).then((_) {}).catchError((exc) { stderr.writeln("Oops, something went wrong: $exc"); exitCode = 1; }); diff --git a/lib/pubspec.update.g.dart b/lib/pubspec.update.g.dart new file mode 100644 index 00000000..b79370e3 --- /dev/null +++ b/lib/pubspec.update.g.dart @@ -0,0 +1,26 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:http/src/base_client.dart' as http; +import 'package:pub_semver/pub_semver.dart'; + +final Version PACKAGE_VERSION = new Version(1, 0, 6); +Future fetchCurrentVersion(http.BaseClient client) async { + var response = + await client.get('https://pub.dartlang.org/api/packages/angel_cli'); + var json = JSON.decode(response.body) as Map; + if (!(json.containsKey('latest')) || + !(json['latest'].containsKey('pubspec')) || + !(json['latest']['pubspec'].containsKey('version'))) { + throw new StateError( + 'GET https://pub.dartlang.org/api/packages/angel_cli returned an invalid pub API response.'); + } + return new Version.parse(json['latest']['pubspec']['version']); +} + +Future checkForUpdate(http.BaseClient client) async { + var current = await fetchCurrentVersion(client); + if (PACKAGE_VERSION.compareTo(current) < 0) { + return current; + } + return null; +} diff --git a/lib/src/commands/init.dart b/lib/src/commands/init.dart index 9a84bfac..744ca6d3 100644 --- a/lib/src/commands/init.dart +++ b/lib/src/commands/init.dart @@ -17,7 +17,9 @@ class InitCommand extends Command { String get description => "Initializes a new Angel project in the current directory."; - InitCommand() {} + InitCommand() { + argParser.addFlag('pub-get', defaultsTo: true); + } @override run() async { @@ -38,15 +40,60 @@ class InitCommand extends Command { new File.fromUri(projectDir.uri.resolve('config/production.yaml')), secret); - var name = p.basenameWithoutExtension(projectDir.path); + var name = p.basenameWithoutExtension( + projectDir.absolute.uri.normalizePath().toFilePath()); print('Renaming project from "angel" to "$name"...'); await renamePubspec(projectDir, 'angel', name); await renameDartFiles(projectDir, 'angel', name); + + if (argResults['pub-get'] != false) { + print('Now running pub get...'); + await _pubGet(projectDir); + } + _pen.green(); - _pen( - "${Icon.CHECKMARK} Successfully initialized Angel project. Now running pub get..."); + _pen("${Icon.CHECKMARK} Successfully initialized Angel project."); _pen(); - await _pubGet(projectDir); + _pen + ..reset() + ..text('\nCongratulations! You are ready to start developing with Angel!') + ..text('\nTo start the server (with file watching), run ') + ..magenta() + ..text('`angel start`') + ..normal() + ..text(' in your terminal.') + ..text('\nHappy coding!') + ..call(); + } + + _deleteRecursive(FileSystemEntity entity, [bool self = true]) async { + if (entity is Directory) { + await for (var entity in entity.list(recursive: true)) { + try { + await _deleteRecursive(entity); + } catch (e) {} + } + + try { + if (self != false) await entity.delete(recursive: true); + } catch (e) {} + } else if (entity is File) { + try { + await entity.delete(recursive: true); + } catch (e) {} + } else if (entity is Link) { + var path = await entity.resolveSymbolicLinks(); + var stat = await FileStat.stat(path); + + switch (stat.type) { + case FileSystemEntityType.DIRECTORY: + return await _deleteRecursive(new Directory(path)); + case FileSystemEntityType.FILE: + return await _deleteRecursive(new File(path)); + default: + break; + } + } } _cloneRepo(Directory projectDir) async { @@ -54,11 +101,16 @@ class InitCommand extends Command { if (await projectDir.exists()) { var chooser = new Chooser(["Yes", "No"], message: - "Directory '${projectDir.absolute.path}' exists. Overwrite it? (Yes/No)"); + "Directory '${projectDir.absolute.path}' already exists. Overwrite it? (Yes/No)"); if (await chooser.choose() != "Yes") throw new Exception("Chose not to overwrite existing directory."); - await projectDir.delete(recursive: true); + else if (projectDir.absolute.uri.normalizePath().toFilePath() != + Directory.current.absolute.uri.normalizePath().toFilePath()) + await projectDir.delete(recursive: true); + else { + await _deleteRecursive(projectDir, false); + } } var git = await Process.start("git", [ @@ -89,7 +141,10 @@ class InitCommand extends Command { } _pubGet(Directory projectDir) async { - var pub = await Process.start("pub", ["get"], + var exec = new File(Platform.resolvedExecutable); + var pubPath = exec.parent.uri.resolve('pub').path; + print('Running pub at "$pubPath"...'); + var pub = await Process.start(pubPath, ["get"], workingDirectory: projectDir.absolute.path); stdout.addStream(pub.stdout); stderr.addStream(pub.stderr); diff --git a/lib/src/commands/plugin.dart b/lib/src/commands/plugin.dart index 6a731cda..fdadffe2 100644 --- a/lib/src/commands/plugin.dart +++ b/lib/src/commands/plugin.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; +import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; class PluginCommand extends Command { final TextPen _pen = new TextPen(); @@ -13,24 +15,24 @@ class PluginCommand extends Command { @override run() async { - final name = await readInput("Name of Plugin: "), lower = name.toLowerCase(); + var pubspec = await PubSpec.load(Directory.current); + final name = await readInput("Name of Plugin: "), + lower = new ReCase(name).snakeCase; final testDir = new Directory("lib/src/config/plugins"); - final pluginFile = new File.fromUri( - testDir.uri.resolve("$lower.dart")); + final pluginFile = new File.fromUri(testDir.uri.resolve("$lower.dart")); - if (!await pluginFile.exists()) - await pluginFile.create(recursive: true); + if (!await pluginFile.exists()) await pluginFile.create(recursive: true); - await pluginFile.writeAsString(_generatePlugin(lower)); + await pluginFile.writeAsString(_generatePlugin(pubspec, name, lower)); _pen.green(); _pen("${Icon.CHECKMARK} Successfully generated plugin $name."); _pen(); } - String _generatePlugin(String name) { - + String _generatePlugin(PubSpec pubspec, String name, String lower) { return ''' +library ${pubspec.name}.plugins.$lower; import 'dart:async'; import 'package:angel_framework/angel_framework.dart'; diff --git a/lib/src/commands/service.dart b/lib/src/commands/service.dart index 71f2945f..dda6e6fb 100644 --- a/lib/src/commands/service.dart +++ b/lib/src/commands/service.dart @@ -71,11 +71,11 @@ class ServiceCommand extends Command { } if (generator.createsModel == true) { - await _generateModel(name, lower); + await _generateModel(pubspec, name, lower); } if (generator.createsValidator == true) { - await _generateValidator(lower, rc.constantCase); + await _generateValidator(pubspec, lower, rc.constantCase); } if (generator.exportedInServiceLibrary == true) { @@ -152,34 +152,33 @@ class ServiceCommand extends Command { return prettyToSource(lib.buildAst()); } - _generateModel(String name, String lower) async { + _generateModel(PubSpec pubspec, String name, String lower) async { var file = new File('lib/src/models/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' - +library ${pubspec.name}.models.$lower; import 'package:angel_framework/common.dart'; class $name extends Model { - String name; - - String desc; + @override + String id; + String name, desc; - $name({String id, this.name, this.desc}) { - this.id = id; - } + $name({this.id, this.name, this.desc}); } ''' .trim()); } - _generateValidator(String lower, String constantCase) async { + _generateValidator(PubSpec pubspec, String lower, String constantCase) async { var file = new File('lib/src/validators/$lower.dart'); if (!await file.exists()) await file.createSync(recursive: true); await file.writeAsString(''' +library ${pubspec.name}.models.$lower; import 'package:angel_validate/angel_validate.dart'; final Validator $constantCase = new Validator({ diff --git a/lib/src/commands/service_generators/rethink.dart b/lib/src/commands/service_generators/rethink.dart index 267e1776..71d6acc5 100644 --- a/lib/src/commands/service_generators/rethink.dart +++ b/lib/src/commands/service_generators/rethink.dart @@ -25,6 +25,7 @@ class RethinkServiceGenerator extends ServiceGenerator { ExpressionBuilder createInstance( MethodBuilder methodBuilder, String name, String lower) { return new TypeBuilder('RethinkService').newInstance([ + reference('connection'), reference('r').invoke('table', [literal(pluralize(lower))]) ]); } diff --git a/lib/src/commands/test.dart b/lib/src/commands/test.dart index 5bc9c046..47c022e0 100644 --- a/lib/src/commands/test.dart +++ b/lib/src/commands/test.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; import "package:console/console.dart"; import 'package:pubspec/pubspec.dart'; +import 'package:recase/recase.dart'; class TestCommand extends Command { final TextPen _pen = new TextPen(); @@ -14,7 +15,8 @@ class TestCommand extends Command { @override run() async { - final name = await readInput("Name of Test: "), lower = name.toLowerCase(); + final name = await readInput("Name of Test: "), + lower = new ReCase(name).snakeCase; final testDir = new Directory("test/services"); final testFile = new File.fromUri(testDir.uri.resolve("${lower}_test.dart")); diff --git a/pubspec.yaml b/pubspec.yaml index ff5a8ab5..b4e736a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,13 +2,14 @@ author: "Tobe O " description: "Command-line tools for the Angel framework." homepage: "https://github.com/angel-dart/angel_cli" name: "angel_cli" -version: "1.0.6" +version: "1.0.7" dependencies: # analyzer: "^0.29.0" args: "^0.13.7" code_builder: "^1.0.0-beta" console: "^2.2.3" glob: "^1.1.0" + http: ^0.11.3 id: "^1.0.0" inflection: "^0.4.1" pubspec: "^0.0.14" @@ -16,6 +17,9 @@ dependencies: recase: "^1.0.0" watcher: "^0.9.7" yaml: "^2.0.0" +dev_dependencies: + build_runner: ^0.3.0 + check_for_update: ^1.0.0 environment: sdk: ">=1.19.0" executables: diff --git a/tool/build.dart b/tool/build.dart new file mode 100644 index 00000000..c07165fd --- /dev/null +++ b/tool/build.dart @@ -0,0 +1,4 @@ +import 'package:build_runner/build_runner.dart'; +import 'phases.dart'; + +main() => build(phaseGroup, deleteFilesByDefault: true); diff --git a/tool/phases.dart b/tool/phases.dart new file mode 100644 index 00000000..1f5fbf61 --- /dev/null +++ b/tool/phases.dart @@ -0,0 +1,6 @@ +import 'package:build_runner/build_runner.dart'; +import 'package:check_for_update/builder.dart'; + +final PhaseGroup phaseGroup = new PhaseGroup.singleAction( + new CheckForUpdateBuilder(subDirectory: 'lib'), + new InputSet('angel_cli', const ['pubspec.yaml'])); diff --git a/tool/watch.dart b/tool/watch.dart new file mode 100644 index 00000000..cd2ce7db --- /dev/null +++ b/tool/watch.dart @@ -0,0 +1,4 @@ +import 'package:build_runner/build_runner.dart'; +import 'phases.dart'; + +main() => watch(phaseGroup, deleteFilesByDefault: true);