Index: utils/pub/command_lish.dart |
diff --git a/utils/pub/command_lish.dart b/utils/pub/command_lish.dart |
index f775b46c58860c778f8b0a55e4a1b65bdf48c0f8..420952c95a879fd98e9780457f2f833a9b8cbdec 100644 |
--- a/utils/pub/command_lish.dart |
+++ b/utils/pub/command_lish.dart |
@@ -10,6 +10,7 @@ import 'dart:uri'; |
import '../../pkg/args/lib/args.dart'; |
import '../../pkg/http/lib/http.dart' as http; |
+import 'directory_tree.dart'; |
import 'git.dart' as git; |
import 'io.dart'; |
import 'log.dart' as log; |
@@ -33,21 +34,13 @@ class LishCommand extends PubCommand { |
/// The URL of the server to which to upload the package. |
Uri get server => new Uri.fromString(commandOptions['server']); |
- Future onRun() { |
+ Future _publish(packageBytes) { |
var cloudStorageUrl; |
return oauth2.withClient(cache, (client) { |
// TODO(nweiz): Cloud Storage can provide an XML-formatted error. We |
// should report that error and exit. |
- return Futures.wait([ |
- client.get(server.resolve("/packages/versions/new.json")), |
- _filesToPublish.transform((files) { |
- log.fine('Archiving and publishing ${entrypoint.root}.'); |
- return createTarGz(files, baseDir: entrypoint.root.dir); |
- }).chain(consumeInputStream), |
- _validate() |
- ]).chain((results) { |
- var response = results[0]; |
- var packageBytes = results[1]; |
+ var newUri = server.resolve("/packages/versions/new.json"); |
+ return client.get(newUri).chain((response) { |
var parameters = _parseJson(response); |
var url = _expectField(parameters, 'url', response); |
@@ -98,19 +91,38 @@ class LishCommand extends PubCommand { |
} |
} else if (e is oauth2.ExpirationException) { |
log.error("Pub's authorization to upload packages has expired and " |
- "can't be automatically refreshed."); |
- return onRun(); |
+ "can't be automatically refreshed."); |
+ return _publish(packageBytes); |
} else if (e is oauth2.AuthorizationException) { |
var message = "OAuth2 authorization failed"; |
if (e.description != null) message = "$message (${e.description})"; |
log.error("$message."); |
- return oauth2.clearCredentials(cache).chain((_) => onRun()); |
+ return oauth2.clearCredentials(cache).chain((_) => |
+ _publish(packageBytes)); |
} else { |
throw e; |
} |
}); |
} |
+ Future onRun() { |
+ var files; |
+ return _filesToPublish.transform((f) { |
+ files = f; |
+ log.fine('Archiving and publishing ${entrypoint.root}.'); |
+ return createTarGz(files, baseDir: entrypoint.root.dir); |
+ }).chain(consumeInputStream).chain((packageBytes) { |
+ // Show the package contents so the user can verify they look OK. |
+ var package = entrypoint.root; |
+ log.message( |
+ 'Publishing "${package.name}" ${package.version}:\n' |
+ '${generateTree(files)}'); |
+ |
+ // Validate the package. |
+ return _validate().chain((_) => _publish(packageBytes)); |
+ }); |
+ } |
+ |
/// The basenames of files that are automatically excluded from archives. |
final _BLACKLISTED_FILES = const ['pubspec.lock']; |
@@ -123,6 +135,14 @@ class LishCommand extends PubCommand { |
/// will return all non-hidden files. |
Future<List<String>> get _filesToPublish { |
var rootDir = entrypoint.root.dir; |
+ |
+ // TODO(rnystrom): listDir() returns real file paths after symlinks are |
+ // resolved. This means if libDir contains a symlink, the resulting paths |
+ // won't appear to be within it, which confuses relativeTo(). Work around |
+ // that here by making sure we have the real path to libDir. Remove this |
+ // when #7346 is fixed. |
+ rootDir = new File(rootDir).fullPathSync(); |
+ |
return Futures.wait([ |
dirExists(join(rootDir, '.git')), |
git.isInstalled |
@@ -135,15 +155,25 @@ class LishCommand extends PubCommand { |
return listDir(rootDir, recursive: true).chain((entries) { |
return Futures.wait(entries.map((entry) { |
- return fileExists(entry).transform((isFile) => isFile ? entry : null); |
+ return fileExists(entry).transform((isFile) { |
+ // Skip directories. |
+ if (!isFile) return null; |
+ |
+ // TODO(rnystrom): Making these relative will break archive |
+ // creation if the cwd is ever *not* the package root directory. |
+ // Should instead only make these relative right before generating |
+ // the tree display (which is what really needs them to be). |
+ // Make it relative to the package root. |
+ return relativeTo(entry, rootDir); |
+ }); |
})); |
}); |
}).transform((files) => files.filter((file) { |
if (file == null || _BLACKLISTED_FILES.contains(basename(file))) { |
return false; |
} |
- return !splitPath(relativeTo(file, rootDir)) |
- .some(_BLACKLISTED_DIRECTORIES.contains); |
+ |
+ return !splitPath(file).some(_BLACKLISTED_DIRECTORIES.contains); |
})); |
} |
@@ -180,15 +210,22 @@ class LishCommand extends PubCommand { |
var errors = pair.first; |
var warnings = pair.last; |
- if (errors.isEmpty && warnings.isEmpty) return new Future.immediate(null); |
- if (!errors.isEmpty) throw "Package validation failed."; |
+ if (!errors.isEmpty) { |
+ throw "Sorry, your package is missing " |
+ "${(errors.length > 1) ? 'some requirements' : 'a requirement'} " |
+ "and can't be published yet.\nFor more information, see: " |
+ "http://pub.dartlang.org/doc/pub-lish.html.\n"; |
+ } |
+ |
+ var message = 'Looks great! Are you ready to upload your package'; |
+ |
+ if (!warnings.isEmpty) { |
+ var s = warnings.length == 1 ? '' : 's'; |
+ message = "Package has ${warnings.length} warning$s. Upload anyway"; |
+ } |
- var s = warnings.length == 1 ? '' : 's'; |
- stdout.writeString("Package has ${warnings.length} warning$s. Upload " |
- "anyway (y/n)? "); |
- return readLine().transform((line) { |
- if (new RegExp(r"^[yY]").hasMatch(line)) return; |
- throw "Package upload canceled."; |
+ return confirm(message).transform((confirmed) { |
+ if (!confirmed) throw "Package upload canceled."; |
}); |
}); |
} |