├── lib ├── scripts.dart └── src │ ├── commands │ ├── commands.dart │ ├── reset.dart │ ├── clean.dart │ ├── load_publock.dart │ ├── ignore.dart │ ├── get.dart │ ├── upgrade.dart │ ├── link.dart │ ├── init.dart │ └── install.dart │ ├── publock.dart │ ├── package.dart │ └── run.dart ├── .idea ├── vcs.xml ├── modules.xml └── scripts.iml ├── pubspec.yaml ├── LICENSE ├── bin └── scripts.dart ├── .gitignore └── README.md /lib/scripts.dart: -------------------------------------------------------------------------------- 1 | library scripts; 2 | 3 | export 'src/commands/commands.dart'; 4 | export 'src/package.dart'; 5 | export 'src/publock.dart'; 6 | export 'src/run.dart'; 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/src/commands/commands.dart: -------------------------------------------------------------------------------- 1 | export 'clean.dart'; 2 | export 'get.dart'; 3 | export 'ignore.dart'; 4 | export 'install.dart'; 5 | export 'init.dart'; 6 | export 'link.dart'; 7 | export 'reset.dart'; 8 | export 'upgrade.dart'; 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/commands/reset.dart: -------------------------------------------------------------------------------- 1 | import 'package:args/command_runner.dart'; 2 | import 'clean.dart'; 3 | import 'get.dart'; 4 | 5 | class ResetCommand extends Command { 6 | @override 7 | String get name => 'reset'; 8 | 9 | @override 10 | String get description => 'Runs `clean`, followed by `get`.'; 11 | 12 | @override 13 | run() async { 14 | await new CleanCommand().run(); 15 | await new GetCommand().run(); 16 | } 17 | } -------------------------------------------------------------------------------- /lib/src/publock.dart: -------------------------------------------------------------------------------- 1 | import 'package:yaml/yaml.dart'; 2 | import 'package.dart'; 3 | 4 | class Publock { 5 | final List packages = []; 6 | 7 | Publock({List packages: const []}) { 8 | this.packages.addAll(packages); 9 | } 10 | 11 | factory Publock.fromMap(Map data) { 12 | final packages = []; 13 | 14 | data['packages'].forEach((k, v) { 15 | packages.add(new PubPackage.fromMap({'name': k}..addAll(v))); 16 | }); 17 | 18 | return new Publock(packages: packages); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: scripts 2 | description: Run commands upon installing Dart packages, and more. 3 | author: Tobe O 4 | version: 1.0.0-dev+1 5 | homepage: https://github.com/thosakwe/dart_scripts 6 | environment: 7 | sdk: ">=1.19.0" 8 | dependencies: 9 | args: ^1.0.0 10 | console: ^2.2.4 11 | http: ^0.11.3 12 | id: ^1.0.15 13 | path: ^1.0.0 14 | pubspec: ^0.0.15 15 | tuple: ^1.0.0 16 | yaml: ^2.1.12 17 | yamlicious: ^0.0.5 18 | dev_dependencies: 19 | test: ^0.12.15 20 | executables: 21 | scripts: scripts -------------------------------------------------------------------------------- /lib/src/commands/clean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/command_runner.dart'; 3 | 4 | class CleanCommand extends Command { 5 | @override 6 | String get name => 'clean'; 7 | 8 | @override 9 | String get description => 'Removes the .scripts-bin directory, if it exists.'; 10 | 11 | @override 12 | run() async { 13 | final scriptsBin = new Directory.fromUri(Directory.current.uri.resolve('./.scripts-bin')); 14 | 15 | if (await scriptsBin.exists()) { 16 | await scriptsBin.delete(recursive: true); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /.idea/scripts.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/src/commands/load_publock.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'package:yaml/yaml.dart'; 4 | import '../publock.dart'; 5 | 6 | final File lockFile = 7 | new File.fromUri(Directory.current.uri.resolve('./pubspec.lock')); 8 | final File pubspecFile = 9 | new File.fromUri(Directory.current.uri.resolve('./pubspec.yaml')); 10 | 11 | _copyVal(v) { 12 | if (v is YamlMap) { 13 | final map = {}; 14 | v.forEach((k, v) => _copy(k, v, map)); 15 | return map; 16 | } else if (v is YamlList) { 17 | return v.map(_copyVal).toList(); 18 | } else 19 | return v; 20 | } 21 | 22 | _copy(String k, v, Map out) { 23 | out[k] = _copyVal(v); 24 | } 25 | 26 | Future loadPublock() async { 27 | return new Publock.fromMap(loadYaml(await lockFile.readAsString())); 28 | } -------------------------------------------------------------------------------- /lib/src/commands/ignore.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'package:args/command_runner.dart'; 4 | 5 | class IgnoreCommand extends Command { 6 | static const String _contents = '.scripts-bin/'; 7 | 8 | @override 9 | String get name => 'ignore'; 10 | 11 | @override 12 | String get description => 'Adds .scripts-bin/ to your VCS ignore file.'; 13 | 14 | IgnoreCommand() { 15 | argParser 16 | ..addOption( 17 | 'filename', 18 | help: 'The name of the ignore file to write to.', 19 | defaultsTo: '.gitignore', 20 | ); 21 | } 22 | 23 | @override 24 | Future run() async { 25 | var ignoreFile = new File.fromUri(Directory.current.uri.resolve(argResults['filename'])); 26 | 27 | if (!await ignoreFile.exists()) { 28 | return await ignoreFile.writeAsString(_contents); 29 | } 30 | 31 | var contents = await ignoreFile.readAsString(); 32 | 33 | if (!contents.contains(_contents)) { 34 | var sink = await ignoreFile.openWrite(mode: FileMode.APPEND); 35 | sink.writeln(_contents); 36 | await sink.close(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Tobe O 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/scripts.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/command_runner.dart'; 3 | import 'package:pubspec/pubspec.dart'; 4 | import 'package:scripts/scripts.dart'; 5 | 6 | main(List args) async { 7 | final runner = new CommandRunner( 8 | 'scripts', 'Run commands upon installing Dart packages, and more.'); 9 | runner.addCommand(new CleanCommand()); 10 | runner.addCommand(new GetCommand()); 11 | runner.addCommand(new IgnoreCommand()); 12 | runner.addCommand(new InitCommand()); 13 | runner.addCommand(new InstallCommand()); 14 | runner.addCommand(new LinkCommand()); 15 | runner.addCommand(new ResetCommand()); 16 | runner.addCommand(new UpgradeCommand()); 17 | 18 | try { 19 | await runner.run(args); 20 | } catch (exc) { 21 | if (exc is UsageException) { 22 | final pubspec = await PubSpec.load(Directory.current); 23 | 24 | for (final arg in args) { 25 | try { 26 | await runScript(pubspec, arg); 27 | } catch (e, st) { 28 | stderr.writeln('ERR: $e'); 29 | stderr.writeln(st); 30 | // stderr.writeln(e); 31 | return exit(1); 32 | } 33 | } 34 | } else { 35 | stderr.writeln(exc); 36 | return exit(1); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/commands/get.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/command_runner.dart'; 3 | import 'package:pubspec/pubspec.dart'; 4 | import 'link.dart'; 5 | import '../run.dart'; 6 | import 'load_publock.dart'; 7 | 8 | class GetCommand extends Command { 9 | String get name => 'get'; 10 | String get description => 11 | 'Runs pub get, and then runs scripts of any dependencies.'; 12 | final LinkCommand _link = new LinkCommand(); 13 | 14 | run() async { 15 | final args = ['get']; 16 | 17 | if (argResults != null) args.addAll(argResults.rest); 18 | 19 | await Process.run('pub', args); 20 | print('Now linking dependencies...'); 21 | await _link.run(); 22 | 23 | final publock = await loadPublock(); 24 | 25 | for (final package in publock.packages) { 26 | final pubspec = await package.readPubspec(); 27 | var unparsed = pubspec.unParsedYaml; 28 | 29 | if (unparsed.containsKey('scripts') && 30 | unparsed['scripts'].containsKey('get')) { 31 | print('Running get hook from package "${package.name}"...'); 32 | await runScript(pubspec, 'get', workingDir: package.location); 33 | } 34 | } 35 | 36 | await runScript(await PubSpec.load(Directory.current), 'get', allowFail: true); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/commands/upgrade.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/command_runner.dart'; 3 | import 'package:pubspec/pubspec.dart'; 4 | import 'link.dart'; 5 | import '../run.dart'; 6 | import 'load_publock.dart'; 7 | 8 | class UpgradeCommand extends Command { 9 | String get name => 'upgrade'; 10 | 11 | String get description => 12 | 'Runs pub upgrade, and then runs scripts of any dependencies.'; 13 | final LinkCommand _link = new LinkCommand(); 14 | 15 | run() async { 16 | final args = ['upgrade']; 17 | 18 | if (argResults != null) args.addAll(argResults.rest); 19 | 20 | await Process.run('pub', args); 21 | print('Now linking dependencies...'); 22 | await _link.run(); 23 | 24 | final publock = await loadPublock(); 25 | 26 | for (final package in publock.packages) { 27 | final pubspec = await package.readPubspec(); 28 | var unparsed = pubspec.unParsedYaml; 29 | 30 | if (unparsed.containsKey('scripts') && 31 | unparsed['scripts'].containsKey('upgrade')) { 32 | print('Running get hook from package "${package.name}"...'); 33 | await runScript(pubspec, 'upgrade', workingDir: package.location); 34 | } 35 | } 36 | 37 | await runScript(await PubSpec.load(Directory.current), 'upgrade', 38 | allowFail: true); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/tools/private-files.html 2 | 3 | # Files and directories created by pub 4 | .buildlog 5 | .packages 6 | .project 7 | .scripts-bin 8 | .pub/ 9 | build/ 10 | **/packages/ 11 | 12 | # Files created by dart2js 13 | # (Most Dart developers will use pub build to compile Dart, use/modify these 14 | # rules if you intend to use dart2js directly 15 | # Convention is to use extension '.dart.js' for Dart compiled to Javascript to 16 | # differentiate from explicit Javascript files) 17 | *.dart.js 18 | *.part.js 19 | *.js.deps 20 | *.js.map 21 | *.info.json 22 | 23 | # Directory created by dartdoc 24 | doc/api/ 25 | 26 | # Don't commit pubspec lock file 27 | # (Library packages only! Remove pattern if developing an application package) 28 | pubspec.lock 29 | ### JetBrains template 30 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 31 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 32 | 33 | # User-specific stuff: 34 | .idea/workspace.xml 35 | .idea/tasks.xml 36 | 37 | # Sensitive or high-churn files: 38 | .idea/dataSources/ 39 | .idea/dataSources.ids 40 | .idea/dataSources.xml 41 | .idea/dataSources.local.xml 42 | .idea/sqlDataSources.xml 43 | .idea/dynamic.xml 44 | .idea/uiDesigner.xml 45 | 46 | # Gradle: 47 | .idea/gradle.xml 48 | .idea/libraries 49 | 50 | # Mongo Explorer plugin: 51 | .idea/mongoSettings.xml 52 | 53 | ## File-based project format: 54 | *.iws 55 | 56 | ## Plugin-specific files: 57 | 58 | # IntelliJ 59 | /out/ 60 | 61 | # mpeltonen/sbt-idea plugin 62 | .idea_modules/ 63 | 64 | # JIRA plugin 65 | atlassian-ide-plugin.xml 66 | 67 | # Crashlytics plugin (for Android Studio and IntelliJ) 68 | com_crashlytics_export_strings.xml 69 | crashlytics.properties 70 | crashlytics-build.properties 71 | fabric.properties 72 | 73 | -------------------------------------------------------------------------------- /lib/src/package.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'package:path/path.dart' as p; 4 | import 'package:pubspec/pubspec.dart'; 5 | 6 | class PubPackage { 7 | static String _pubCachePath; 8 | final PackageDescription description; 9 | final String name, source, version; 10 | 11 | PubPackage({this.name, this.description, this.source, this.version}); 12 | 13 | static String get pubCachePath { 14 | if (_pubCachePath != null) return _pubCachePath; 15 | 16 | if (Platform.isWindows) { 17 | var appdata = Platform.environment['APPDATA']; 18 | return _pubCachePath = p.join(appdata, 'Pub', 'Cache'); 19 | } 20 | 21 | var homeDir = Platform.environment['HOME']; 22 | return _pubCachePath = p.join(homeDir, '.pub-cache'); 23 | } 24 | 25 | factory PubPackage.fromMap(Map data) { 26 | return new PubPackage( 27 | name: data['name'], 28 | description: new PackageDescription.fromMap(data['description']), 29 | source: data['source'], 30 | version: data['version']); 31 | } 32 | 33 | Directory get location { 34 | if (source == 'hosted') { 35 | return new Directory( 36 | p.join(pubCachePath, 'hosted', 'pub.dartlang.org', '$name-$version')); 37 | } else 38 | throw new UnsupportedError( 39 | 'Unsupported source for package $name in pubspec.lock: $source'); 40 | } 41 | 42 | Future readPubspec() { 43 | return PubSpec.load(location); 44 | } 45 | 46 | Map toJson() { 47 | return { 48 | 'name': name, 49 | 'description': description.toJson(), 50 | 'source': source, 51 | 'version': version 52 | }; 53 | } 54 | } 55 | 56 | class PackageDescription { 57 | final String name, url; 58 | 59 | PackageDescription({this.name, this.url}); 60 | 61 | factory PackageDescription.fromMap(Map data) { 62 | return new PackageDescription(name: data['name'], url: data['url']); 63 | } 64 | 65 | Map toJson() { 66 | return {'name': name, 'url': url}; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/run.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'package:pubspec/pubspec.dart'; 5 | 6 | runScript(PubSpec pubspec, String script, 7 | {bool allowFail: false, Directory workingDir}) async { 8 | final scripts = pubspec.unParsedYaml['scripts'] ?? {}; 9 | 10 | if (!scripts.containsKey(script)) { 11 | if (allowFail) 12 | return; 13 | else 14 | throw new Exception( 15 | 'Could not find a script named "$script" in project "${pubspec.name ?? ''}".'); 16 | } else { 17 | final lines = scripts[script] is List ? scripts[script] : [scripts[script]]; 18 | 19 | for (final line in lines) { 20 | final result = await runLine(line, workingDir ?? Directory.current); 21 | final String sout = result[1].trim(), serr = result[2].trim(); 22 | 23 | if (sout.isNotEmpty) print(sout); 24 | 25 | if (serr.isNotEmpty) stderr.writeln(serr); 26 | 27 | if (result[0] != 0) { 28 | throw new Exception( 29 | 'Script "$script" failed with exit code ${result[0]}.'); 30 | } 31 | } 32 | } 33 | } 34 | 35 | Future runLine(String line, Directory workingDir) async { 36 | var path = Platform.environment['PATH']; 37 | final scriptsBin = 38 | new Directory.fromUri(Directory.current.uri.resolve('./.scripts-bin')); 39 | 40 | if (Platform.isWindows) { 41 | if (await scriptsBin.exists()) path = '${scriptsBin.absolute.uri};$path'; 42 | 43 | final cmd = await Process.start('cmd', [], 44 | environment: {'PATH': path}, 45 | workingDirectory: workingDir.absolute.path); 46 | cmd.stdin.writeln(line); 47 | cmd.stdin.writeln('exit 0'); 48 | cmd.stdin.flush(); 49 | 50 | return [ 51 | await cmd.exitCode, 52 | await cmd.stdout.transform(UTF8.decoder).join(), 53 | await cmd.stderr.transform(UTF8.decoder).join() 54 | ]; 55 | } else { 56 | if (await scriptsBin.exists()) path = '"${scriptsBin.absolute.uri}":$path'; 57 | 58 | final bash = await Process.start('bash', [], 59 | environment: {'PATH': path}, 60 | workingDirectory: workingDir.absolute.path); 61 | bash.stdin.writeln(line); 62 | bash.stdin.writeln('exit 0'); 63 | bash.stdin.flush(); 64 | 65 | return [ 66 | await bash.exitCode, 67 | await bash.stdout.transform(UTF8.decoder).join(), 68 | await bash.stderr.transform(UTF8.decoder).join() 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/commands/link.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/command_runner.dart'; 3 | import 'load_publock.dart'; 4 | 5 | final RegExp _file = new RegExp(r'^file://'); 6 | final String dartPath = Platform.executable; 7 | 8 | class LinkCommand extends Command { 9 | String get name => 'link'; 10 | String get description => 'Links installed packages to executables.'; 11 | 12 | createBashFile(Directory bin, String name, String scriptFile) async { 13 | final file = new File.fromUri(bin.uri.resolve('$name')); 14 | await file.writeAsString('#!/usr/bin/env bash\n"$dartPath" "$scriptFile" @*'); 15 | await Process.run('chmod', ['+x', file.path]); 16 | } 17 | 18 | createBatFile(Directory bin, String name, String scriptFile) async { 19 | final file = new File.fromUri(bin.uri.resolve('$name.bat')); 20 | await file.writeAsString('@echo off\n"$dartPath" "$scriptFile" %*'); 21 | } 22 | 23 | run() async { 24 | final lock = await loadPublock(); 25 | final Directory bin = 26 | new Directory.fromUri(Directory.current.uri.resolve('./.scripts-bin')); 27 | final packageRoot = 28 | new Directory.fromUri(bin.uri.resolve('./package-root')); 29 | 30 | if (!await packageRoot.exists()) await packageRoot.create(recursive: true); 31 | 32 | for (final pkg in lock.packages) { 33 | // Create symlink 34 | final pubspec = await pkg.readPubspec(); 35 | final link = 36 | new Link.fromUri(packageRoot.uri.resolve('./${pubspec.name}')); 37 | 38 | if (await link.exists()) await link.delete(); 39 | 40 | final uri = pkg.location.absolute.uri.resolve('./lib').toString().replaceAll(_file, ''); 41 | await link.create(uri); 42 | } 43 | 44 | for (final pkg in lock.packages) { 45 | final pubspec = await pkg.readPubspec(); 46 | final Map executables = pubspec.unParsedYaml['executables']; 47 | 48 | if (executables != null) { 49 | for (final name in executables.keys) { 50 | final scriptName = 51 | executables[name].isNotEmpty ? executables[name] : name; 52 | final scriptFile = new File.fromUri( 53 | pkg.location.uri.resolve('./bin/$scriptName.dart')); 54 | final path = scriptFile.absolute.path; 55 | 56 | // Generate snapshot 57 | final snapshot = 58 | new File.fromUri(pkg.location.uri.resolve('$name.snapshot.dart')); 59 | final result = await Process.run(Platform.executable, [ 60 | '--snapshot=${snapshot.path}', 61 | '--package-root=${packageRoot.path}', 62 | path 63 | ]); 64 | 65 | if (result.stderr.isNotEmpty) { 66 | stderr.writeln(result.stderr); 67 | throw new Exception( 68 | "Could not create snapshot for package '$name'."); 69 | } 70 | 71 | // Create script files 72 | await createBashFile(bin, name, snapshot.absolute.path); 73 | await createBatFile(bin, name, snapshot.absolute.path); 74 | print('Successfully linked executables.'); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/src/commands/init.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/command_runner.dart'; 3 | import 'package:console/console.dart'; 4 | import 'package:id/id.dart'; 5 | import 'package:path/path.dart' as path; 6 | import 'package:pubspec/pubspec.dart'; 7 | import 'get.dart'; 8 | import 'load_publock.dart'; 9 | 10 | class InitCommand extends Command { 11 | final GetCommand _get = new GetCommand(); 12 | 13 | @override 14 | String get name => 'init'; 15 | 16 | @override 17 | String get description => 18 | 'Generates a `pubspec.yaml` file in the current directory, after a series of prompts.'; 19 | 20 | @override 21 | run() async { 22 | if (await pubspecFile.exists()) { 23 | throw new Exception( 24 | '`pubspec.yaml` already exists in ${Directory.current.absolute 25 | .uri}.'); 26 | } 27 | 28 | print('This utility will walk you through creating a pubspec.yaml file.'); 29 | print( 30 | ' It only covers the most common items, and tries to guess sensible defaults.'); 31 | print(''); 32 | print('Use `scripts install ` afterwards to install a package and'); 33 | print('save it as a dependency in the pubspec.yaml file.'); 34 | 35 | final pubspec = {}; 36 | 37 | pubspec['name'] = await promptForName(); 38 | pubspec['version'] = await promptForVersion(); 39 | 40 | await promptField(pubspec, 'description'); 41 | await promptField(pubspec, 'author', defaultValue: Platform.localHostname); 42 | 43 | var gitUrl = null; 44 | 45 | try { 46 | final result = await Process.run('git', ['remote', 'get-url', 'origin']); 47 | 48 | if (result.exitCode == 0) { 49 | final stdout = await result.stdout.trim(); 50 | 51 | if (stdout.isNotEmpty) gitUrl = stdout; 52 | } 53 | } catch (e) {} 54 | 55 | await promptField(pubspec, 'homepage', defaultValue: gitUrl); 56 | 57 | print('About to write to ${pubspecFile.absolute.path}:'); 58 | 59 | final result = await readInput('Is this ok? (yes) '); 60 | 61 | if (result.toLowerCase().startsWith('y') || result.trim().isEmpty) { 62 | var ps = new PubSpec.fromJson(pubspec); 63 | await ps.save(Directory.current); 64 | } 65 | 66 | await Process.run('pub', ['get']); 67 | await _get.run(); 68 | } 69 | 70 | promptField(Map pubspec, String field, 71 | {String as, String defaultValue}) async { 72 | final value = await readInput( 73 | defaultValue != null ? '$field ($defaultValue): ' : '$field: '); 74 | 75 | if (value.isNotEmpty) 76 | pubspec[as ?? field] = value; 77 | else if (defaultValue != null) pubspec[as ?? field] = defaultValue; 78 | } 79 | 80 | promptForName() async { 81 | final id = idFromString( 82 | path.basename(Directory.current.path).replaceAll('-', '_')); 83 | final defaultName = id.snake; 84 | final name = await readInput('name: ($defaultName) '); 85 | return name.isNotEmpty ? name : defaultName; 86 | } 87 | 88 | promptForVersion() async { 89 | final version = await readInput('version: (1.0.0) '); 90 | return version.isNotEmpty ? version : '1.0.0'; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scripts 2 | Run commands upon installing Dart packages, and more. 3 | It would be nice if all of these features were 4 | eventually integrated into the main `pub` executable. 5 | Until then, this will do. 6 | 7 | * [Usage](#usage) 8 | * [Running your own Scripts](#running-your-own-scripts) 9 | * [Available Commands](#available-commands) 10 | * [clean](#clean) 11 | * [get, upgrade](#get) 12 | * [link](#link) 13 | * [init](#init) 14 | * [install](#install) 15 | * [reset](#reset) 16 | 17 | # Usage 18 | ```bash 19 | $ pub global activate scripts 20 | ``` 21 | 22 | 23 | To use packages that integrate with `scripts`, you should run 24 | `scripts get` instead of `pub get`. This will run `pub get`, and 25 | then install package executables into a `.scripts-bin` directory. 26 | Then, all installed packages will have their `get` scripts run. 27 | 28 | Also replace `pub upgrade` with `scripts upgrade`. This will run `get` 29 | scripts as well. 30 | 31 | You can run `scripts link` to link executables into `.scripts-bin`. 32 | 33 | # Running your own Scripts 34 | It is very likely that you want to run your own scripts during 35 | development, or upon package installation. Do as follows in your 36 | `pubspec.yaml`: 37 | 38 | ```yaml 39 | name: foo 40 | # ... 41 | scripts: 42 | build: gcc -o foo src/foo.cc 43 | get: 44 | - dart_gyp configure 45 | - dart_gyp build 46 | ``` 47 | 48 | Installed dependencies with executables will automatically be 49 | filled in to the `PATH` during script execution. 50 | 51 | Then, in your project root, you can run: 52 | ```bash 53 | $ scripts build 54 | ``` 55 | 56 | # Available Commands 57 | * [clean](#clean) 58 | * [get, upgrade](#get) 59 | * [link](#link) 60 | * [init](#init) 61 | * [install](#install) 62 | * [reset](#reset) 63 | 64 | ## clean 65 | Removes the `.scripts-bin` directory, if present. 66 | 67 | ## get 68 | This script simply runs `pub get`, and then calls 69 | [`link`](#link). 70 | 71 | ## init 72 | Essentially an `npm init` for Dart. This command will 73 | run you through a series of prompts, after which a `pubspec.yaml` 74 | will be generated for you. 75 | 76 | ## install 77 | Can be used to install dependencies without having to search 78 | the Pub directory for the current version. 79 | 80 | ```bash 81 | # Install the newest version, and apply caret syntax 82 | $ scripts install my-package 83 | 84 | # Install a specific version 85 | $ scripts install my-package@^1.0.0 86 | $ scripts install my-package@0.0.4+25 87 | $ scripts install "my-package@>=2.0.0 <3.0.0" 88 | 89 | # Install a Git dependency 90 | $ scripts install my-package@git://path/to/repo.git 91 | 92 | # Specify a commit or ref 93 | $ scripts install my-package@git://path/to/repo.git#bleeding-edge 94 | 95 | # Install a local package 96 | $ scripts install my-package@path:/Users/john/Source/Dart/pkg 97 | 98 | # Install multiple packages 99 | $ scripts install my-package my-other-package yet-another-package 100 | 101 | # Install to dev_dependencies 102 | $ scripts install --dev http test my-package@git://repo#dev 103 | 104 | # Preview new `pubspec.yaml`, without actually installing dependencies, 105 | # or modifying the file. 106 | $ scripts install --dry-run my-experimental-package 107 | ``` 108 | 109 | ## link 110 | Creates symlinks to each dependency (in future versions, I 111 | will eliminate symlink use), and also creates executable files 112 | linked to any dependencies that export executables. 113 | 114 | ## reset 115 | Runs `clean`, followed by `get`. -------------------------------------------------------------------------------- /lib/src/commands/install.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'package:args/command_runner.dart'; 5 | import 'package:http/http.dart' as http; 6 | import 'package:path/path.dart' as p; 7 | import 'package:pubspec/pubspec.dart'; 8 | import 'package:pub_semver/pub_semver.dart'; 9 | import 'package:tuple/tuple.dart'; 10 | import 'package:yamlicious/yamlicious.dart'; 11 | import 'get.dart'; 12 | 13 | final RegExp _gitPkg = new RegExp(r'^([^@]+)@git://([^#]+)(#(.+))?$'); 14 | final RegExp _pathPkg = new RegExp(r'^([^@]+)@path:([^$]+)$'); 15 | 16 | const String pubApiRoot = 'https://pub.dartlang.org'; 17 | 18 | class InstallCommand extends Command { 19 | final http.Client _client = new http.Client(); 20 | final GetCommand _get = new GetCommand(); 21 | 22 | @override 23 | String get name => 'install'; 24 | 25 | @override 26 | String get description => 27 | 'Adds the specified dependencies to your pubspec.yaml, then runs `scripts get`.'; 28 | 29 | InstallCommand() { 30 | argParser 31 | ..addFlag('dev', 32 | help: 'Installs the package(s) into dev_dependencies.', 33 | defaultsTo: false, 34 | negatable: false) 35 | ..addFlag('dry-run', 36 | help: 37 | 'Resolves new dependencies, and prints the new pubspec, without changing any files.', 38 | defaultsTo: false, 39 | negatable: false); 40 | } 41 | 42 | run() async { 43 | if (argResults.rest.isEmpty) { 44 | stderr.writeln('usage: scripts install [options...] []'); 45 | stderr.writeln(); 46 | stderr.writeln('Options:'); 47 | stderr.writeln(argParser.usage); 48 | return exit(1); 49 | } else { 50 | var pubspec = await PubSpec.load(Directory.current); 51 | var targetMap = {}; 52 | 53 | for (final pkg in argResults.rest) { 54 | if (_gitPkg.hasMatch(pkg)) { 55 | final match = _gitPkg.firstMatch(pkg); 56 | final dep = await resolveGitDep(match); 57 | targetMap[match.group(1)] = dep; 58 | } else if (_pathPkg.hasMatch(pkg)) { 59 | final match = _pathPkg.firstMatch(pkg); 60 | targetMap[match.group(1)] = new PathReference(match.group(2)); 61 | } else { 62 | final dep = await resolvePubDep(pkg, pubApiRoot); 63 | targetMap[dep.item1] = dep.item2; 64 | } 65 | } 66 | 67 | _client.close(); 68 | 69 | if (!argResults['dev']) { 70 | var dependencies = new Map.from(pubspec.dependencies) 71 | ..addAll(targetMap); 72 | pubspec = pubspec.copy(dependencies: dependencies); 73 | } else { 74 | var devDependencies = new Map.from(pubspec.devDependencies) 75 | ..addAll(targetMap); 76 | pubspec = pubspec.copy(devDependencies: devDependencies); 77 | } 78 | 79 | if (argResults['dry-run']) { 80 | return print(toYamlString(pubspec.toJson())); 81 | } else { 82 | await pubspec.save(Directory.current); 83 | print('Now installing dependencies...'); 84 | return await _get.run(); 85 | } 86 | } 87 | } 88 | 89 | GitReference resolveGitDep(Match match) { 90 | if (match.group(4) != null) { 91 | return new GitReference(match.group(2), match.group(4)); 92 | } else { 93 | return new GitReference(match.group(2), null); 94 | } 95 | } 96 | 97 | /// Install [pkg] from [apiRoot]. 98 | Future> resolvePubDep( 99 | String pkg, String apiRoot) async { 100 | final index = pkg.indexOf('@'); 101 | String name; 102 | VersionConstraint version; 103 | 104 | if (index > 0 && index < pkg.length - 1) { 105 | final split = pkg.split('@'); 106 | name = split[0]; 107 | version = new VersionConstraint.parse(split[1]); 108 | } else { 109 | // Try to auto-detect version... 110 | final response = await _client.get(p.join(apiRoot, 'packages', pkg)); 111 | 112 | if (response.statusCode == HttpStatus.NOT_FOUND) { 113 | throw new Exception('Unable to resolve package within pub: "$pkg"'); 114 | } else { 115 | final versions = resolvePackageVersions(JSON.decode(response.body)); 116 | 117 | if (versions.isEmpty) { 118 | throw new Exception( 119 | 'Could not resolve any version of pub package "$pkg".'); 120 | } else { 121 | name = pkg; 122 | version = versions.first; 123 | } 124 | } 125 | } 126 | 127 | DependencyReference out; 128 | 129 | if (apiRoot == pubApiRoot) 130 | out = new HostedReference(version); 131 | else 132 | out = new ExternalHostedReference(name, apiRoot, version); 133 | 134 | return new Tuple2(name, out); 135 | } 136 | 137 | List resolvePackageVersions(Map response) { 138 | return response['versions'].map((Map info) { 139 | String version = info['version']; 140 | return new VersionConstraint.parse('^$version'); 141 | }).toList(); 142 | } 143 | } 144 | --------------------------------------------------------------------------------