Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(787)

Side by Side Diff: lib/src/source/git.dart

Issue 2044253003: Refactor Source and SourceRegistry. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Rename LiveSource to BoundSource. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/source/cached.dart ('k') | lib/src/source/hosted.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 import 'dart:async'; 5 import 'dart:async';
6 6
7 import 'package:path/path.dart' as path; 7 import 'package:path/path.dart' as path;
8 import 'package:pub_semver/pub_semver.dart'; 8 import 'package:pub_semver/pub_semver.dart';
9 9
10 import '../git.dart' as git; 10 import '../git.dart' as git;
11 import '../io.dart'; 11 import '../io.dart';
12 import '../log.dart' as log; 12 import '../log.dart' as log;
13 import '../package.dart'; 13 import '../package.dart';
14 import '../pubspec.dart'; 14 import '../pubspec.dart';
15 import '../source.dart';
16 import '../system_cache.dart';
15 import '../utils.dart'; 17 import '../utils.dart';
16 import 'cached.dart'; 18 import 'cached.dart';
17 19
18 /// A package source that gets packages from Git repos. 20 /// A package source that gets packages from Git repos.
19 class GitSource extends CachedSource { 21 class GitSource extends Source {
22 final name = "git";
23
24 BoundGitSource bind(SystemCache systemCache) =>
25 new BoundGitSource(this, systemCache);
26
20 /// Returns a reference to a git package with the given [name] and [url]. 27 /// Returns a reference to a git package with the given [name] and [url].
21 /// 28 ///
22 /// If passed, [reference] is the Git reference. It defaults to `"HEAD"`. 29 /// If passed, [reference] is the Git reference. It defaults to `"HEAD"`.
23 static PackageRef refFor(String name, String url, {String reference}) => 30 PackageRef refFor(String name, String url, {String reference}) =>
24 new PackageRef(name, "git", {'url': url, 'ref': reference ?? 'HEAD'}); 31 new PackageRef(name, "git", {'url': url, 'ref': reference ?? 'HEAD'});
25 32
26 /// Given a valid git package description, returns the URL of the repository 33 /// Given a valid git package description, returns the URL of the repository
27 /// it pulls from. 34 /// it pulls from.
28 static String urlFromDescription(description) => description["url"]; 35 String urlFromDescription(description) => description["url"];
29
30 final name = "git";
31
32 /// The paths to the canonical clones of repositories for which "git fetch"
33 /// has already been run during this run of pub.
34 final _updatedRepos = new Set<String>();
35
36 /// Given a Git repo that contains a pub package, gets the name of the pub
37 /// package.
38 Future<String> getPackageNameFromRepo(String repo) {
39 // Clone the repo to a temp directory.
40 return withTempDir((tempDir) {
41 return _clone(repo, tempDir, shallow: true).then((_) {
42 var pubspec = new Pubspec.load(tempDir, systemCache.sources);
43 return pubspec.name;
44 });
45 });
46 }
47
48 Future<List<PackageId>> doGetVersions(PackageRef ref) async {
49 await _ensureRepoCache(ref);
50 var path = _repoCachePath(ref);
51 var revision = await _firstRevision(path, ref.description['ref']);
52 var pubspec = await _describeUncached(ref, revision);
53
54 return [
55 new PackageId(ref.name, name, pubspec.version, {
56 'url': ref.description['url'],
57 'ref': ref.description['ref'],
58 'resolved-ref': revision
59 })
60 ];
61 }
62
63 /// Since we don't have an easy way to read from a remote Git repo, this
64 /// just installs [id] into the system cache, then describes it from there.
65 Future<Pubspec> describeUncached(PackageId id) =>
66 _describeUncached(id.toRef(), id.description['resolved-ref']);
67
68 /// Like [describeUncached], but takes a separate [ref] and Git [revision]
69 /// rather than a single ID.
70 Future<Pubspec> _describeUncached(PackageRef ref, String revision) async {
71 await _ensureRevision(ref, revision);
72 var path = _repoCachePath(ref);
73
74 var lines;
75 try {
76 lines = await git.run(["show", "$revision:pubspec.yaml"],
77 workingDir: path);
78 } on git.GitException catch (_) {
79 fail('Could not find a file named "pubspec.yaml" in '
80 '${ref.description['url']} $revision.');
81 }
82
83 return new Pubspec.parse(lines.join("\n"), systemCache.sources,
84 expectedName: ref.name);
85 }
86
87 /// Clones a Git repo to the local filesystem.
88 ///
89 /// The Git cache directory is a little idiosyncratic. At the top level, it
90 /// contains a directory for each commit of each repository, named `<package
91 /// name>-<commit hash>`. These are the canonical package directories that are
92 /// linked to from the `packages/` directory.
93 ///
94 /// In addition, the Git system cache contains a subdirectory named `cache/`
95 /// which contains a directory for each separate repository URL, named
96 /// `<package name>-<url hash>`. These are used to check out the repository
97 /// itself; each of the commit-specific directories are clones of a directory
98 /// in `cache/`.
99 Future<Package> downloadToSystemCache(PackageId id) async {
100 var ref = id.toRef();
101 if (!git.isInstalled) {
102 fail("Cannot get ${id.name} from Git (${ref.description['url']}).\n"
103 "Please ensure Git is correctly installed.");
104 }
105
106 ensureDir(path.join(systemCacheRoot, 'cache'));
107 await _ensureRevision(ref, id.description['resolved-ref']);
108
109 var revisionCachePath = getDirectory(id);
110 if (!entryExists(revisionCachePath)) {
111 await _clone(_repoCachePath(ref), revisionCachePath);
112 await _checkOut(revisionCachePath, id.description['resolved-ref']);
113 }
114
115 return new Package.load(id.name, revisionCachePath, systemCache.sources);
116 }
117
118 /// Returns the path to the revision-specific cache of [id].
119 String getDirectory(PackageId id) => path.join(
120 systemCacheRoot, "${id.name}-${id.description['resolved-ref']}");
121 36
122 PackageRef parseRef(String name, description, {String containingPath}) { 37 PackageRef parseRef(String name, description, {String containingPath}) {
123 // TODO(rnystrom): Handle git URLs that are relative file paths (#8570). 38 // TODO(rnystrom): Handle git URLs that are relative file paths (#8570).
124 if (description is String) description = {'url': description}; 39 if (description is String) description = {'url': description};
125 40
126 if (description is! Map) { 41 if (description is! Map) {
127 throw new FormatException("The description must be a Git URL or a map " 42 throw new FormatException("The description must be a Git URL or a map "
128 "with a 'url' key."); 43 "with a 'url' key.");
129 } 44 }
130 45
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
210 if (description1['url'] != description2['url']) return false; 125 if (description1['url'] != description2['url']) return false;
211 if (description1['ref'] != description2['ref']) return false; 126 if (description1['ref'] != description2['ref']) return false;
212 127
213 if (description1.containsKey('resolved-ref') && 128 if (description1.containsKey('resolved-ref') &&
214 description2.containsKey('resolved-ref')) { 129 description2.containsKey('resolved-ref')) {
215 return description1['resolved-ref'] == description2['resolved-ref']; 130 return description1['resolved-ref'] == description2['resolved-ref'];
216 } 131 }
217 132
218 return true; 133 return true;
219 } 134 }
135 }
136
137 /// The [BoundSource] for [GitSource].
138 class BoundGitSource extends CachedSource {
139 final GitSource source;
140
141 final SystemCache systemCache;
142
143 BoundGitSource(this.source, this.systemCache);
144
145 /// The paths to the canonical clones of repositories for which "git fetch"
146 /// has already been run during this run of pub.
147 final _updatedRepos = new Set<String>();
148
149 /// Given a Git repo that contains a pub package, gets the name of the pub
150 /// package.
151 Future<String> getPackageNameFromRepo(String repo) {
152 // Clone the repo to a temp directory.
153 return withTempDir((tempDir) {
154 return _clone(repo, tempDir, shallow: true).then((_) {
155 var pubspec = new Pubspec.load(tempDir, systemCache.sources);
156 return pubspec.name;
157 });
158 });
159 }
160
161 Future<List<PackageId>> doGetVersions(PackageRef ref) async {
162 await _ensureRepoCache(ref);
163 var path = _repoCachePath(ref);
164 var revision = await _firstRevision(path, ref.description['ref']);
165 var pubspec = await _describeUncached(ref, revision);
166
167 return [
168 new PackageId(ref.name, source.name, pubspec.version, {
169 'url': ref.description['url'],
170 'ref': ref.description['ref'],
171 'resolved-ref': revision
172 })
173 ];
174 }
175
176 /// Since we don't have an easy way to read from a remote Git repo, this
177 /// just installs [id] into the system cache, then describes it from there.
178 Future<Pubspec> describeUncached(PackageId id) =>
179 _describeUncached(id.toRef(), id.description['resolved-ref']);
180
181 /// Like [describeUncached], but takes a separate [ref] and Git [revision]
182 /// rather than a single ID.
183 Future<Pubspec> _describeUncached(PackageRef ref, String revision) async {
184 await _ensureRevision(ref, revision);
185 var path = _repoCachePath(ref);
186
187 var lines;
188 try {
189 lines = await git.run(["show", "$revision:pubspec.yaml"],
190 workingDir: path);
191 } on git.GitException catch (_) {
192 fail('Could not find a file named "pubspec.yaml" in '
193 '${ref.description['url']} $revision.');
194 }
195
196 return new Pubspec.parse(lines.join("\n"), systemCache.sources,
197 expectedName: ref.name);
198 }
199
200 /// Clones a Git repo to the local filesystem.
201 ///
202 /// The Git cache directory is a little idiosyncratic. At the top level, it
203 /// contains a directory for each commit of each repository, named `<package
204 /// name>-<commit hash>`. These are the canonical package directories that are
205 /// linked to from the `packages/` directory.
206 ///
207 /// In addition, the Git system cache contains a subdirectory named `cache/`
208 /// which contains a directory for each separate repository URL, named
209 /// `<package name>-<url hash>`. These are used to check out the repository
210 /// itself; each of the commit-specific directories are clones of a directory
211 /// in `cache/`.
212 Future<Package> downloadToSystemCache(PackageId id) async {
213 var ref = id.toRef();
214 if (!git.isInstalled) {
215 fail("Cannot get ${id.name} from Git (${ref.description['url']}).\n"
216 "Please ensure Git is correctly installed.");
217 }
218
219 ensureDir(path.join(systemCacheRoot, 'cache'));
220 await _ensureRevision(ref, id.description['resolved-ref']);
221
222 var revisionCachePath = getDirectory(id);
223 if (!entryExists(revisionCachePath)) {
224 await _clone(_repoCachePath(ref), revisionCachePath);
225 await _checkOut(revisionCachePath, id.description['resolved-ref']);
226 }
227
228 return new Package.load(id.name, revisionCachePath, systemCache.sources);
229 }
230
231 /// Returns the path to the revision-specific cache of [id].
232 String getDirectory(PackageId id) => path.join(
233 systemCacheRoot, "${id.name}-${id.description['resolved-ref']}");
220 234
221 List<Package> getCachedPackages() { 235 List<Package> getCachedPackages() {
222 // TODO(keertip): Implement getCachedPackages(). 236 // TODO(keertip): Implement getCachedPackages().
223 throw new UnimplementedError( 237 throw new UnimplementedError(
224 "The git source doesn't support listing its cached packages yet."); 238 "The git source doesn't support listing its cached packages yet.");
225 } 239 }
226 240
227 /// Resets all cached packages back to the pristine state of the Git 241 /// Resets all cached packages back to the pristine state of the Git
228 /// repository at the revision they are pinned to. 242 /// repository at the revision they are pinned to.
229 Future<Pair<List<PackageId>, List<PackageId>>> repairCachedPackages() async { 243 Future<Pair<List<PackageId>, List<PackageId>>> repairCachedPackages() async {
230 if (!dirExists(systemCacheRoot)) return new Pair([], []); 244 if (!dirExists(systemCacheRoot)) return new Pair([], []);
231 245
232 var successes = []; 246 var successes = [];
233 var failures = []; 247 var failures = [];
234 248
235 var packages = listDir(systemCacheRoot) 249 var packages = listDir(systemCacheRoot)
236 .where((entry) => dirExists(path.join(entry, ".git"))) 250 .where((entry) => dirExists(path.join(entry, ".git")))
237 .map((packageDir) => new Package.load(null, packageDir, 251 .map((packageDir) => new Package.load(
238 systemCache.sources)) 252 null, packageDir, systemCache.sources))
239 .toList(); 253 .toList();
240 254
241 // Note that there may be multiple packages with the same name and version 255 // Note that there may be multiple packages with the same name and version
242 // (pinned to different commits). The sort order of those is unspecified. 256 // (pinned to different commits). The sort order of those is unspecified.
243 packages.sort(Package.orderByNameAndVersion); 257 packages.sort(Package.orderByNameAndVersion);
244 258
245 for (var package in packages) { 259 for (var package in packages) {
246 var id = new PackageId(package.name, this.name, package.version, null); 260 var id = new PackageId(package.name, source.name, package.version, null);
247 261
248 log.message("Resetting Git repository for " 262 log.message("Resetting Git repository for "
249 "${log.bold(package.name)} ${package.version}..."); 263 "${log.bold(package.name)} ${package.version}...");
250 264
251 try { 265 try {
252 // Remove all untracked files. 266 // Remove all untracked files.
253 await git.run(["clean", "-d", "--force", "-x"], 267 await git.run(["clean", "-d", "--force", "-x"],
254 workingDir: package.dir); 268 workingDir: package.dir);
255 269
256 // Discard all changes to tracked files. 270 // Discard all changes to tracked files.
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 (result) => null); 376 (result) => null);
363 } 377 }
364 378
365 /// Returns the path to the canonical clone of the repository referred to by 379 /// Returns the path to the canonical clone of the repository referred to by
366 /// [id] (the one in `<system cache>/git/cache`). 380 /// [id] (the one in `<system cache>/git/cache`).
367 String _repoCachePath(PackageRef ref) { 381 String _repoCachePath(PackageRef ref) {
368 var repoCacheName = '${ref.name}-${sha1(ref.description['url'])}'; 382 var repoCacheName = '${ref.name}-${sha1(ref.description['url'])}';
369 return path.join(systemCacheRoot, 'cache', repoCacheName); 383 return path.join(systemCacheRoot, 'cache', repoCacheName);
370 } 384 }
371 } 385 }
OLDNEW
« no previous file with comments | « lib/src/source/cached.dart ('k') | lib/src/source/hosted.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698