diff --git a/lib/src/command/global.dart b/lib/src/command/global.dart index cfd2996f2616dd43ce0cf45785ed2b6a89bf73ff..aa1ee2493d4ec33d775f0d66623614bc23c8a79d 100644 --- a/lib/src/command/global.dart +++ b/lib/src/command/global.dart @@ -6,6 +6,7 @@ library pub.command.global; import '../command.dart'; import 'global_activate.dart'; +import 'global_deactivate.dart'; /// Handles the `global` pub command. class GlobalCommand extends PubCommand { @@ -13,6 +14,7 @@ class GlobalCommand extends PubCommand { String get usage => "pub global <subcommand>"; final subcommands = { - "activate": new GlobalActivateCommand() + "activate": new GlobalActivateCommand(), + "deactivate": new GlobalDeactivateCommand() }; } diff --git a/lib/src/command/global_deactivate.dart b/lib/src/command/global_deactivate.dart new file mode 100644 index 0000000000000000000000000000000000000000..047067aae30bbad80fc61125b39a332496d84da4 --- /dev/null +++ b/lib/src/command/global_deactivate.dart @@ -0,0 +1,35 @@ +// 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. + +library pub.command.global_deactivate; + +import 'dart:async'; + +import '../command.dart'; +import '../utils.dart'; +import '../version.dart'; + +/// Handles the `global deactivate` pub command. +class GlobalDeactivateCommand extends PubCommand { + String get description => "Remove a previously activated package."; + String get usage => "pub global deactivate <package>"; + bool get requiresEntrypoint => false; + bool get takesArguments => true; + + Future onRun() { + // Make sure there is a package. + if (commandOptions.rest.isEmpty) { + usageError("No package to deactivate given."); + } + + // Don't allow extra arguments. + if (commandOptions.rest.length > 1) { + var unexpected = commandOptions.rest.skip(1).map((arg) => '"$arg"'); + var arguments = pluralize("argument", unexpected.length); + usageError("Unexpected $arguments ${toSentence(unexpected)}."); + } + + globals.deactivate(commandOptions.rest.first); + } +} diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index c0ab46f8e23d315b090b3b5214f6919ad29777c1..eb4864f0fc8f75b6f0d796cce2a631748d929a2f 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -5,9 +5,10 @@ library pub.global_packages; import 'dart:async'; +import 'dart:convert'; import 'dart:io'; -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as p; import 'io.dart'; import 'lock_file.dart'; @@ -33,7 +34,7 @@ class GlobalPackages { final SystemCache cache; /// The directory where the lockfiles for activated packages are stored. - String get _directory => path.join(cache.rootDir, "global_packages"); + String get _directory => p.join(cache.rootDir, "global_packages"); /// The source that global packages can be activated from. // TODO(rnystrom): Allow activating packages from other sources. @@ -49,7 +50,7 @@ class GlobalPackages { /// Finds the latest version of the hosted package with [name] that matches /// [constraint] and makes it the active global version. Future activate(String name, VersionConstraint constraint) { - var lockFilePath = path.join(_directory, name + ".lock"); + var lockFilePath = p.join(_directory, name + ".lock"); // See if we already have it activated. var lockFile; @@ -106,6 +107,22 @@ class GlobalPackages { }); } + /// Deactivates a previously-activated package named [name] or fails with + /// an error if [name] is not an active package. + void deactivate(String name) { + // See if we already have it activated. + try { + var lockFilePath = p.join(_directory, "$name.lock"); + var lockFile = new LockFile.load(lockFilePath, cache.sources); + var version = lockFile.packages[name].version; + + deleteEntry(lockFilePath); + log.message("Deactivated package ${log.bold(name)} $version."); + } on IOException catch (error) { + dataError("No active package ${log.bold(name)}."); + } + } + /// Picks the best version of [package] to activate that meets [constraint]. /// /// If [version] is not `null`, this tries to maintain that version if diff --git a/test/global/activate/unknown_package_test.dart b/test/global/activate/unknown_package_test.dart index 5348fe849384110376df23af7117c7b5d4ab3069..4a6e3a6dd6317542f52478763db00bc092f62000 100644 --- a/test/global/activate/unknown_package_test.dart +++ b/test/global/activate/unknown_package_test.dart @@ -4,6 +4,7 @@ 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'; @@ -14,6 +15,6 @@ main() { schedulePub(args: ["global", "activate", "foo"], error: startsWith("Could not find package foo at"), - exitCode: 1); + exitCode: exit_codes.UNAVAILABLE); }); } diff --git a/test/global/deactivate/deactivate_and_reactivate_package_test.dart b/test/global/deactivate/deactivate_and_reactivate_package_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..0e04d187419cbe0e6e613de60c9227b2b7eea597 --- /dev/null +++ b/test/global/deactivate/deactivate_and_reactivate_package_test.dart @@ -0,0 +1,32 @@ +// 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('activates a different version after deactivating', () { + servePackages([ + packageMap("foo", "1.0.0"), + packageMap("foo", "2.0.0") + ]); + + // Activate an old version. + schedulePub(args: ["global", "activate", "foo", "1.0.0"]); + + schedulePub(args: ["global", "deactivate", "foo"], + output: "Deactivated package foo 1.0.0."); + + // Activating again should forget the old version. + schedulePub(args: ["global", "activate", "foo"], output: """ +Downloading foo 2.0.0... +Resolving dependencies... +Activated foo 2.0.0. + """); + }); +} diff --git a/test/global/deactivate/deactivate_package_test.dart b/test/global/deactivate/deactivate_package_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..f6e9056422b46b34bed4048316f6f2f94124074d --- /dev/null +++ b/test/global/deactivate/deactivate_package_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('deactivates an active package', () { + servePackages([ + packageMap("foo", "1.0.0") + ]); + + schedulePub(args: ["global", "activate", "foo"]); + + schedulePub(args: ["global", "deactivate", "foo"], + output: "Deactivated package foo 1.0.0."); + }); +} diff --git a/test/global/deactivate/missing_package_arg_test.dart b/test/global/deactivate/missing_package_arg_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..33a2a1b86e8c7b5acb7e5a273f99ec34fb696ca5 --- /dev/null +++ b/test/global/deactivate/missing_package_arg_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 '../../../lib/src/exit_codes.dart' as exit_codes; +import '../../test_pub.dart'; + +main() { + initConfig(); + integration('fails if no package was given', () { + schedulePub(args: ["global", "deactivate"], + error: """ + No package to deactivate given. + + Usage: pub global deactivate <package> + -h, --help Print usage information for this command. + """, + exitCode: exit_codes.USAGE); + }); +} diff --git a/test/global/deactivate/unexpected_arguments_test.dart b/test/global/deactivate/unexpected_arguments_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..9057ebf24c32ac76801ea16a5eda7ef0476e5d9c --- /dev/null +++ b/test/global/deactivate/unexpected_arguments_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 '../../../lib/src/exit_codes.dart' as exit_codes; +import '../../test_pub.dart'; + +main() { + initConfig(); + integration('fails if there are extra arguments', () { + schedulePub(args: ["global", "deactivate", "foo", "bar", "baz"], + error: """ + Unexpected arguments "bar" and "baz". + + Usage: pub global deactivate <package> + -h, --help Print usage information for this command. + """, + exitCode: exit_codes.USAGE); + }); +} diff --git a/test/global/deactivate/unknown_package_test.dart b/test/global/deactivate/unknown_package_test.dart new file mode 100644 index 0000000000000000000000000000000000000000..3860c48023c6a04ac6e147f5832370466aff6c07 --- /dev/null +++ b/test/global/deactivate/unknown_package_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 '../../../lib/src/exit_codes.dart' as exit_codes; +import '../../descriptor.dart' as d; +import '../../test_pub.dart'; + +main() { + initConfig(); + integration('errors if the package is not activated', () { + servePackages([]); + + schedulePub(args: ["global", "deactivate", "foo"], + error: "No active package foo.", + exitCode: exit_codes.DATA); + }); +}