| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library git_source; | 5 library git_source; |
| 6 | 6 |
| 7 import 'dart:async'; |
| 7 import 'git.dart' as git; | 8 import 'git.dart' as git; |
| 8 import 'io.dart'; | 9 import 'io.dart'; |
| 9 import 'package.dart'; | 10 import 'package.dart'; |
| 10 import 'source.dart'; | 11 import 'source.dart'; |
| 11 import 'source_registry.dart'; | 12 import 'source_registry.dart'; |
| 12 import 'utils.dart'; | 13 import 'utils.dart'; |
| 13 | 14 |
| 14 /// A package source that installs packages from Git repos. | 15 /// A package source that installs packages from Git repos. |
| 15 class GitSource extends Source { | 16 class GitSource extends Source { |
| 16 final String name = "git"; | 17 final String name = "git"; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 27 /// linked to from the `packages/` directory. | 28 /// linked to from the `packages/` directory. |
| 28 /// | 29 /// |
| 29 /// In addition, the Git system cache contains a subdirectory named `cache/` | 30 /// In addition, the Git system cache contains a subdirectory named `cache/` |
| 30 /// which contains a directory for each separate repository URL, named | 31 /// which contains a directory for each separate repository URL, named |
| 31 /// `<package name>-<url hash>`. These are used to check out the repository | 32 /// `<package name>-<url hash>`. These are used to check out the repository |
| 32 /// itself; each of the commit-specific directories are clones of a directory | 33 /// itself; each of the commit-specific directories are clones of a directory |
| 33 /// in `cache/`. | 34 /// in `cache/`. |
| 34 Future<Package> installToSystemCache(PackageId id) { | 35 Future<Package> installToSystemCache(PackageId id) { |
| 35 var revisionCachePath; | 36 var revisionCachePath; |
| 36 | 37 |
| 37 return git.isInstalled.chain((installed) { | 38 return git.isInstalled.then((installed) { |
| 38 if (!installed) { | 39 if (!installed) { |
| 39 throw new Exception( | 40 throw new Exception( |
| 40 "Cannot install '${id.name}' from Git (${_getUrl(id)}).\n" | 41 "Cannot install '${id.name}' from Git (${_getUrl(id)}).\n" |
| 41 "Please ensure Git is correctly installed."); | 42 "Please ensure Git is correctly installed."); |
| 42 } | 43 } |
| 43 | 44 |
| 44 return ensureDir(join(systemCacheRoot, 'cache')); | 45 return ensureDir(join(systemCacheRoot, 'cache')); |
| 45 }).chain((_) => _ensureRepoCache(id)) | 46 }).then((_) => _ensureRepoCache(id)) |
| 46 .chain((_) => _revisionCachePath(id)) | 47 .then((_) => _revisionCachePath(id)) |
| 47 .chain((path) { | 48 .then((path) { |
| 48 revisionCachePath = path; | 49 revisionCachePath = path; |
| 49 return exists(revisionCachePath); | 50 return exists(revisionCachePath); |
| 50 }).chain((exists) { | 51 }).then((exists) { |
| 51 if (exists) return new Future.immediate(null); | 52 if (exists) return new Future.immediate(null); |
| 52 return _clone(_repoCachePath(id), revisionCachePath, mirror: false); | 53 return _clone(_repoCachePath(id), revisionCachePath, mirror: false); |
| 53 }).chain((_) { | 54 }).then((_) { |
| 54 var ref = _getEffectiveRef(id); | 55 var ref = _getEffectiveRef(id); |
| 55 if (ref == 'HEAD') return new Future.immediate(null); | 56 if (ref == 'HEAD') return new Future.immediate(null); |
| 56 return _checkOut(revisionCachePath, ref); | 57 return _checkOut(revisionCachePath, ref); |
| 57 }).chain((_) { | 58 }).then((_) { |
| 58 return Package.load(id.name, revisionCachePath, systemCache.sources); | 59 return Package.load(id.name, revisionCachePath, systemCache.sources); |
| 59 }); | 60 }); |
| 60 } | 61 } |
| 61 | 62 |
| 62 /// Ensures [description] is a Git URL. | 63 /// Ensures [description] is a Git URL. |
| 63 void validateDescription(description, {bool fromLockFile: false}) { | 64 void validateDescription(description, {bool fromLockFile: false}) { |
| 64 // A single string is assumed to be a Git URL. | 65 // A single string is assumed to be a Git URL. |
| 65 if (description is String) return; | 66 if (description is String) return; |
| 66 if (description is! Map || !description.containsKey('url')) { | 67 if (description is! Map || !description.containsKey('url')) { |
| 67 throw new FormatException("The description must be a Git URL or a map " | 68 throw new FormatException("The description must be a Git URL or a map " |
| (...skipping 16 matching lines...) Expand all Loading... |
| 84 bool descriptionsEqual(description1, description2) { | 85 bool descriptionsEqual(description1, description2) { |
| 85 // TODO(nweiz): Do we really want to throw an error if you have two | 86 // TODO(nweiz): Do we really want to throw an error if you have two |
| 86 // dependencies on some repo, one of which specifies a ref and one of which | 87 // dependencies on some repo, one of which specifies a ref and one of which |
| 87 // doesn't? If not, how do we handle that case in the version solver? | 88 // doesn't? If not, how do we handle that case in the version solver? |
| 88 return _getUrl(description1) == _getUrl(description2) && | 89 return _getUrl(description1) == _getUrl(description2) && |
| 89 _getRef(description1) == _getRef(description2); | 90 _getRef(description1) == _getRef(description2); |
| 90 } | 91 } |
| 91 | 92 |
| 92 /// Attaches a specific commit to [id] to disambiguate it. | 93 /// Attaches a specific commit to [id] to disambiguate it. |
| 93 Future<PackageId> resolveId(PackageId id) { | 94 Future<PackageId> resolveId(PackageId id) { |
| 94 return _revisionAt(id).transform((revision) { | 95 return _revisionAt(id).then((revision) { |
| 95 var description = {'url': _getUrl(id), 'ref': _getRef(id)}; | 96 var description = {'url': _getUrl(id), 'ref': _getRef(id)}; |
| 96 description['resolved-ref'] = revision; | 97 description['resolved-ref'] = revision; |
| 97 return new PackageId(id.name, this, id.version, description); | 98 return new PackageId(id.name, this, id.version, description); |
| 98 }); | 99 }); |
| 99 } | 100 } |
| 100 | 101 |
| 101 /// Ensure that the canonical clone of the repository referred to by [id] (the | 102 /// Ensure that the canonical clone of the repository referred to by [id] (the |
| 102 /// one in `<system cache>/git/cache`) exists and is up-to-date. Returns a | 103 /// one in `<system cache>/git/cache`) exists and is up-to-date. Returns a |
| 103 /// future that completes once this is finished and throws an exception if it | 104 /// future that completes once this is finished and throws an exception if it |
| 104 /// fails. | 105 /// fails. |
| 105 Future _ensureRepoCache(PackageId id) { | 106 Future _ensureRepoCache(PackageId id) { |
| 106 var path = _repoCachePath(id); | 107 var path = _repoCachePath(id); |
| 107 return exists(path).chain((exists) { | 108 return exists(path).then((exists) { |
| 108 if (!exists) return _clone(_getUrl(id), path, mirror: true); | 109 if (!exists) return _clone(_getUrl(id), path, mirror: true); |
| 109 | 110 |
| 110 return git.run(["fetch"], workingDir: path).transform((result) => null); | 111 return git.run(["fetch"], workingDir: path).then((result) => null); |
| 111 }); | 112 }); |
| 112 } | 113 } |
| 113 | 114 |
| 114 /// Returns a future that completes to the revision hash of [id]. | 115 /// Returns a future that completes to the revision hash of [id]. |
| 115 Future<String> _revisionAt(PackageId id) { | 116 Future<String> _revisionAt(PackageId id) { |
| 116 return git.run(["rev-parse", _getEffectiveRef(id)], | 117 return git.run(["rev-parse", _getEffectiveRef(id)], |
| 117 workingDir: _repoCachePath(id)).transform((result) => result[0]); | 118 workingDir: _repoCachePath(id)).then((result) => result[0]); |
| 118 } | 119 } |
| 119 | 120 |
| 120 /// Returns the path to the revision-specific cache of [id]. | 121 /// Returns the path to the revision-specific cache of [id]. |
| 121 Future<String> _revisionCachePath(PackageId id) { | 122 Future<String> _revisionCachePath(PackageId id) { |
| 122 return _revisionAt(id).transform((rev) { | 123 return _revisionAt(id).then((rev) { |
| 123 var revisionCacheName = '${id.name}-$rev'; | 124 var revisionCacheName = '${id.name}-$rev'; |
| 124 return join(systemCacheRoot, revisionCacheName); | 125 return join(systemCacheRoot, revisionCacheName); |
| 125 }); | 126 }); |
| 126 } | 127 } |
| 127 | 128 |
| 128 /// Clones the repo at the URI [from] to the path [to] on the local | 129 /// Clones the repo at the URI [from] to the path [to] on the local |
| 129 /// filesystem. | 130 /// filesystem. |
| 130 /// | 131 /// |
| 131 /// If [mirror] is true, create a bare, mirrored clone. This doesn't check out | 132 /// If [mirror] is true, create a bare, mirrored clone. This doesn't check out |
| 132 /// the working tree, but instead makes the repository a local mirror of the | 133 /// the working tree, but instead makes the repository a local mirror of the |
| 133 /// remote repository. See the manpage for `git clone` for more information. | 134 /// remote repository. See the manpage for `git clone` for more information. |
| 134 Future _clone(String from, String to, {bool mirror: false}) { | 135 Future _clone(String from, String to, {bool mirror: false}) { |
| 135 // Git on Windows does not seem to automatically create the destination | 136 // Git on Windows does not seem to automatically create the destination |
| 136 // directory. | 137 // directory. |
| 137 return ensureDir(to).chain((_) { | 138 return ensureDir(to).then((_) { |
| 138 var args = ["clone", from, to]; | 139 var args = ["clone", from, to]; |
| 139 if (mirror) args.insertRange(1, 1, "--mirror"); | 140 if (mirror) args.insertRange(1, 1, "--mirror"); |
| 140 return git.run(args); | 141 return git.run(args); |
| 141 }).transform((result) => null); | 142 }).then((result) => null); |
| 142 } | 143 } |
| 143 | 144 |
| 144 /// Checks out the reference [ref] in [repoPath]. | 145 /// Checks out the reference [ref] in [repoPath]. |
| 145 Future _checkOut(String repoPath, String ref) { | 146 Future _checkOut(String repoPath, String ref) { |
| 146 return git.run(["checkout", ref], workingDir: repoPath).transform( | 147 return git.run(["checkout", ref], workingDir: repoPath).then( |
| 147 (result) => null); | 148 (result) => null); |
| 148 } | 149 } |
| 149 | 150 |
| 150 /// Returns the path to the canonical clone of the repository referred to by | 151 /// Returns the path to the canonical clone of the repository referred to by |
| 151 /// [id] (the one in `<system cache>/git/cache`). | 152 /// [id] (the one in `<system cache>/git/cache`). |
| 152 String _repoCachePath(PackageId id) { | 153 String _repoCachePath(PackageId id) { |
| 153 var repoCacheName = '${id.name}-${sha1(_getUrl(id))}'; | 154 var repoCacheName = '${id.name}-${sha1(_getUrl(id))}'; |
| 154 return join(systemCacheRoot, 'cache', repoCacheName); | 155 return join(systemCacheRoot, 'cache', repoCacheName); |
| 155 } | 156 } |
| 156 | 157 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 return description['ref']; | 191 return description['ref']; |
| 191 } | 192 } |
| 192 | 193 |
| 193 /// Returns [description] if it's a description, or [PackageId.description] if | 194 /// Returns [description] if it's a description, or [PackageId.description] if |
| 194 /// it's a [PackageId]. | 195 /// it's a [PackageId]. |
| 195 _getDescription(description) { | 196 _getDescription(description) { |
| 196 if (description is PackageId) return description.description; | 197 if (description is PackageId) return description.description; |
| 197 return description; | 198 return description; |
| 198 } | 199 } |
| 199 } | 200 } |
| OLD | NEW |