platform/lib/src/commands/init.dart
2018-12-31 11:29:40 -05:00

254 lines
7.6 KiB
Dart

import 'dart:async';
import "dart:io";
import "package:args/command_runner.dart";
import 'package:io/ansi.dart';
import 'package:path/path.dart' as p;
import 'package:prompts/prompts.dart' as prompts;
import 'package:recase/recase.dart';
import '../random_string.dart' as rs;
import '../util.dart';
import 'key.dart';
import 'pub.dart';
import 'rename.dart';
class InitCommand extends Command {
final KeyCommand _key = new KeyCommand();
@override
String get name => "init";
@override
String get description =>
"Initializes a new Angel project in the current directory.";
InitCommand() {
argParser..addFlag('pub-get', defaultsTo: true);
}
@override
run() async {
Directory projectDir = new Directory(
argResults.arguments.isEmpty ? "." : argResults.arguments[0]);
print("Creating new Angel project in ${projectDir.absolute.path}...");
await _cloneRepo(projectDir);
// await preBuild(projectDir);
var secret = rs.randomAlphaNumeric(32);
print('Generated new development JWT secret: $secret');
await _key.changeSecret(
new File.fromUri(projectDir.uri.resolve('config/default.yaml')),
secret);
secret = rs.randomAlphaNumeric(32);
print('Generated new production JWT secret: $secret');
await _key.changeSecret(
new File.fromUri(projectDir.uri.resolve('config/production.yaml')),
secret);
var name = p.basenameWithoutExtension(
projectDir.absolute.uri.normalizePath().toFilePath());
name = ReCase(name).snakeCase;
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);
}
print(green.wrap("$checkmark Successfully initialized Angel project."));
stdout
..writeln()
..writeln(
'Congratulations! You are ready to start developing with Angel!')
..write('To start the server (with ')
..write(cyan.wrap('hot-reloading'))
..write('), run ')
..write(magenta.wrap('`dart --observe bin/dev.dart`'))
..writeln(' in your terminal.')
..writeln()
..writeln('Find more documentation about Angel:')
..writeln(' * https://angel-dart.github.io')
..writeln(' * https://github.com/angel-dart/angel/wiki')
..writeln(
' * https://www.youtube.com/playlist?list=PLl3P3tmiT-frEV50VdH_cIrA2YqIyHkkY')
..writeln(' * https://medium.com/the-angel-framework')
..writeln(' * https://dart.academy/tag/angel')
..writeln()
..writeln('Happy coding!');
}
_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 {
try {
if (await projectDir.exists()) {
var shouldDelete = prompts.getBool(
"Directory '${projectDir.absolute.path}' already exists. Overwrite it?");
if (!shouldDelete)
throw "Chose not to overwrite existing directory.";
else if (projectDir.absolute.uri.normalizePath().toFilePath() !=
Directory.current.absolute.uri.normalizePath().toFilePath())
await projectDir.delete(recursive: true);
else {
await _deleteRecursive(projectDir, false);
}
}
var boilerplate = basicBoilerplate;
// print('Choose a project type before continuing:');
// var boilerplate = prompts.choose(
// 'Choose a project type before continuing', boilerplates);
print(
'Cloning "${boilerplate.name}" boilerplate from "${boilerplate.url}"...');
Process git;
if (boilerplate.ref == null) {
git = await Process.start("git", [
"clone",
"--depth",
"1",
boilerplate.url,
projectDir.absolute.path
]);
} else {
// git clone --single-branch -b branch host:/dir.git
git = await Process.start("git", [
"clone",
"--depth",
"1",
"--single-branch",
"-b",
boilerplate.ref,
boilerplate.url,
projectDir.absolute.path
]);
}
stdout.addStream(git.stdout);
stderr.addStream(git.stderr);
if (await git.exitCode != 0) {
throw new Exception("Could not clone repo.");
}
/*
if (boilerplate.ref != null) {
git = await Process
.start("git", ["checkout", 'origin/${boilerplate.ref}']);
stdout.addStream(git.stdout);
stderr.addStream(git.stderr);
if (await git.exitCode != 0) {
throw new Exception("Could not checkout branch ${boilerplate.ref}.");
}
}
*/
if (boilerplate.needsPrebuild) {
await preBuild(projectDir).catchError((_) => null);
}
var gitDir = new Directory.fromUri(projectDir.uri.resolve(".git"));
if (await gitDir.exists()) await gitDir.delete(recursive: true);
} catch (e) {
if (e is! String) {
print(red.wrap("$ballot Could not initialize Angel project."));
}
rethrow;
}
}
_pubGet(Directory projectDir) async {
var pubPath = resolvePub();
print('Running pub at "$pubPath"...');
var pub = await Process.start(pubPath, ["get"],
workingDirectory: projectDir.absolute.path,
mode: ProcessStartMode.inheritStdio);
var code = await pub.exitCode;
print("Pub process exited with code $code");
}
}
Future preBuild(Directory projectDir) async {
// Run build
print('Running `pub run build_runner build`...');
var build = await Process.start(resolvePub(), ['run', 'build'],
workingDirectory: projectDir.absolute.path);
stdout.addStream(build.stdout);
stderr.addStream(build.stderr);
var buildCode = await build.exitCode;
if (buildCode != 0) throw new Exception('Failed to pre-build resources.');
}
const BoilerplateInfo ormBoilerplate = const BoilerplateInfo(
'ORM',
"A starting point for applications that use Angel's ORM.",
'https://github.com/angel-dart/boilerplate_orm.git',
);
const BoilerplateInfo basicBoilerplate = const BoilerplateInfo(
'Basic',
'Minimal starting point for Angel 2.x - A simple server with only a few additional packages.',
'https://github.com/angel-dart/angel.git');
const BoilerplateInfo legacyBoilerplate = const BoilerplateInfo(
'Legacy',
'Minimal starting point for applications running Angel 1.1.x.',
'https://github.com/angel-dart/angel.git',
ref: '1.1.x',
);
const List<BoilerplateInfo> boilerplates = const [
basicBoilerplate,
//legacyBoilerplate,
//ormBoilerplate,
];
class BoilerplateInfo {
final String name, description, url, ref;
final bool needsPrebuild;
const BoilerplateInfo(this.name, this.description, this.url,
{this.ref, this.needsPrebuild: false});
@override
String toString() => '$name ($description)';
}