├── 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 |
--------------------------------------------------------------------------------