From fca3108c1c2156d85cbfca97af6161f8805f1978 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" <rnystrom@google.com> Date: Mon, 4 Aug 2014 21:16:00 +0000 Subject: [PATCH] Add publishTo to pubspec and use it in pub lish. RELNOTE=Pubspecs can have a "publishTo" field which is either a URL to specify the default server that publishing should use or "none" to specify that the package is private and cannot be published. BUG=https://code.google.com/p/dart/issues/detail?id=20046 R=nweiz@google.com Review URL: https://codereview.chromium.org//417043005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge@38880 260f80e4-7a28-3924-810f-c04153c831b5 --- lib/src/command/lish.dart | 21 ++++++++++- lib/src/pubspec.dart | 36 +++++++++++++++++++ .../does_not_publish_if_private_test.dart | 22 ++++++++++++ ...blish_if_private_with_server_arg_test.dart | 23 ++++++++++++ ...orce_does_not_publish_if_private_test.dart | 22 ++++++++++++ test/lish/preview_errors_if_private_test.dart | 22 ++++++++++++ ...er_arg_does_not_override_private_test.dart | 22 ++++++++++++ ...ver_arg_overrides_publish_to_url_test.dart | 20 +++++++++++ test/lish/uses_publish_to_url_test.dart | 20 +++++++++++ test/pubspec_test.dart | 31 ++++++++++++++++ 10 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 test/lish/does_not_publish_if_private_test.dart create mode 100644 test/lish/does_not_publish_if_private_with_server_arg_test.dart create mode 100644 test/lish/force_does_not_publish_if_private_test.dart create mode 100644 test/lish/preview_errors_if_private_test.dart create mode 100644 test/lish/server_arg_does_not_override_private_test.dart create mode 100644 test/lish/server_arg_overrides_publish_to_url_test.dart create mode 100644 test/lish/uses_publish_to_url_test.dart diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index a58ae3c6..82635056 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart @@ -26,7 +26,20 @@ class LishCommand extends PubCommand { List<String> get aliases => const ["lish", "lush"]; /// The URL of the server to which to upload the package. - Uri get server => Uri.parse(commandOptions['server']); + Uri get server { + // An explicit argument takes precedence. + if (commandOptions.wasParsed('server')) { + return Uri.parse(commandOptions['server']); + } + + // Otherwise, use the one specified in the pubspec. + if (entrypoint.root.pubspec.publishTo != null) { + return Uri.parse(entrypoint.root.pubspec.publishTo); + } + + // Otherwise, use the default. + return Uri.parse(HostedSource.defaultUrl); + } /// Whether the publish is just a preview. bool get dryRun => commandOptions['dry-run']; @@ -98,6 +111,12 @@ class LishCommand extends PubCommand { usageError('Cannot use both --force and --dry-run.'); } + if (entrypoint.root.pubspec.isPrivate) { + dataError('A private package cannot be published.\n' + 'You can enable this by changing the "publishTo" field in your ' + 'pubspec.'); + } + var files = entrypoint.root.listFiles(); log.fine('Archiving and publishing ${entrypoint.root}.'); diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index 883a77c9..e9ea5966 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart @@ -209,6 +209,41 @@ class Pubspec { } PubspecEnvironment _environment; + /// The URL of the server that the package should default to being published + /// to, "none" if the package should not be published, or `null` if it should + /// be published to the default server. + /// + /// If this does return a URL string, it will be a valid parseable URL. + String get publishTo { + if (_parsedPublishTo) return _publishTo; + + var publishTo = fields['publishTo']; + if (publishTo != null) { + var span = fields.nodes['publishTo'].span; + + if (publishTo is! String) { + _error('"publishTo" field must be a string.', span); + } + + // It must be "none" or a valid URL. + if (publishTo != "none") { + _wrapFormatException('"publishTo" field', span, + () => Uri.parse(publishTo)); + } + } + + _parsedPublishTo = true; + _publishTo = publishTo; + return _publishTo; + } + bool _parsedPublishTo = false; + String _publishTo; + + /// Whether the package is private and cannot be published. + /// + /// This is specified in the pubspec by setting "publishTo" to "none". + bool get isPrivate => publishTo == "none"; + /// Whether or not the pubspec has no contents. bool get isEmpty => name == null && version == Version.none && dependencies.isEmpty; @@ -304,6 +339,7 @@ class Pubspec { _getError(() => this.devDependencies); _getError(() => this.transformers); _getError(() => this.environment); + _getError(() => this.publishTo); return errors; } diff --git a/test/lish/does_not_publish_if_private_test.dart b/test/lish/does_not_publish_if_private_test.dart new file mode 100644 index 00000000..69e843ec --- /dev/null +++ b/test/lish/does_not_publish_if_private_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../../lib/src/exit_codes.dart' as exit_codes; +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('does not publish if the package is private', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "none"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish"], + error: startsWith("A private package cannot be published."), + exitCode: exit_codes.DATA); + }); +} diff --git a/test/lish/does_not_publish_if_private_with_server_arg_test.dart b/test/lish/does_not_publish_if_private_with_server_arg_test.dart new file mode 100644 index 00000000..f5dbcc73 --- /dev/null +++ b/test/lish/does_not_publish_if_private_with_server_arg_test.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../../lib/src/exit_codes.dart' as exit_codes; +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('does not publish if the package is private even if a server ' + 'argument is provided', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "none"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish", "--server", "http://example.com"], + error: startsWith("A private package cannot be published."), + exitCode: exit_codes.DATA); + }); +} diff --git a/test/lish/force_does_not_publish_if_private_test.dart b/test/lish/force_does_not_publish_if_private_test.dart new file mode 100644 index 00000000..b92ea728 --- /dev/null +++ b/test/lish/force_does_not_publish_if_private_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../../lib/src/exit_codes.dart' as exit_codes; +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('force does not publish if the package is private', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "none"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish", "--force"], + error: startsWith("A private package cannot be published."), + exitCode: exit_codes.DATA); + }); +} diff --git a/test/lish/preview_errors_if_private_test.dart b/test/lish/preview_errors_if_private_test.dart new file mode 100644 index 00000000..cc538a7c --- /dev/null +++ b/test/lish/preview_errors_if_private_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../../lib/src/exit_codes.dart' as exit_codes; +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('preview shows an error if the package is private', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "none"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish", "--dry-run"], + error: startsWith("A private package cannot be published."), + exitCode: exit_codes.DATA); + }); +} diff --git a/test/lish/server_arg_does_not_override_private_test.dart b/test/lish/server_arg_does_not_override_private_test.dart new file mode 100644 index 00000000..d3a56674 --- /dev/null +++ b/test/lish/server_arg_does_not_override_private_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../../lib/src/exit_codes.dart' as exit_codes; +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('an explicit --server argument does not override privacy', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "none"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish", "--server", "http://arg.com"], + error: startsWith("A private package cannot be published."), + exitCode: exit_codes.DATA); + }); +} diff --git a/test/lish/server_arg_overrides_publish_to_url_test.dart b/test/lish/server_arg_overrides_publish_to_url_test.dart new file mode 100644 index 00000000..5c04d664 --- /dev/null +++ b/test/lish/server_arg_overrides_publish_to_url_test.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('an explicit --server argument overrides a "publishTo" url', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "http://pubspec.com"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish", "--dry-run", "--server", "http://arg.com"], + output: contains("http://arg.com")); + }); +} diff --git a/test/lish/uses_publish_to_url_test.dart b/test/lish/uses_publish_to_url_test.dart new file mode 100644 index 00000000..ae011fe0 --- /dev/null +++ b/test/lish/uses_publish_to_url_test.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:scheduled_test/scheduled_test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +main() { + initConfig(); + integration('preview shows an error if the package is private', () { + var pkg = packageMap("test_pkg", "1.0.0"); + pkg["publishTo"] = "http://example.com"; + d.dir(appPath, [d.pubspec(pkg)]).create(); + + schedulePub(args: ["lish", "--dry-run"], + output: contains("Publishing test_pkg 1.0.0 to http://example.com")); + }); +} diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart index 1d38c6c5..41e6d7c9 100644 --- a/test/pubspec_test.dart +++ b/test/pubspec_test.dart @@ -413,5 +413,36 @@ environment: (pubspec) => pubspec.environment); }); }); + + group("publishTo", () { + test("defaults to null if omitted", () { + var pubspec = new Pubspec.parse('', sources); + expect(pubspec.publishTo, isNull); + }); + + test("throws if not a string", () { + expectPubspecException('publishTo: 123', + (pubspec) => pubspec.publishTo); + }); + + test("allows a URL", () { + var pubspec = new Pubspec.parse(''' +publishTo: http://example.com +''', sources); + expect(pubspec.publishTo, equals("http://example.com")); + }); + + test("allows none", () { + var pubspec = new Pubspec.parse(''' +publishTo: none +''', sources); + expect(pubspec.publishTo, equals("none")); + }); + + test("throws on other strings", () { + expectPubspecException('publishTo: http://bad.url:not-port', + (pubspec) => pubspec.publishTo); + }); + }); }); } -- GitLab