-
Notifications
You must be signed in to change notification settings - Fork 229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add github version range support #1335
Changes from 5 commits
f5feece
bb0fb5a
aad3a12
93213d1
922ac77
1a9120c
3d9c0ab
59f2e6f
8492ba2
01e6787
673dcd5
c935354
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ library pub.source.git; | |
import 'dart:async'; | ||
|
||
import 'package:path/path.dart' as path; | ||
import 'package:pub_semver/pub_semver.dart'; | ||
|
||
import '../git.dart' as git; | ||
import '../io.dart'; | ||
|
@@ -40,6 +41,49 @@ class GitSource extends CachedSource { | |
}); | ||
} | ||
|
||
/// Gets the list of all versions from git tags. | ||
/// Anything that Version.parse understands is considered a version, | ||
/// it will also attempt to strip off a preceding 'v' e.g. v1.0.0 | ||
Future<List<Pubspec>> getVersions(String name, description) async { | ||
if (!_useVersionTags(description)) { | ||
// No need to get versions if the useVersionTags option is false | ||
return super.getVersions(name, description); | ||
} | ||
|
||
PackageId id = | ||
new PackageId(name, this.name, null, _getDescription(description)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Passing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed this in computmaxer@1a9120c |
||
String cachePath = _repoCachePath(id); | ||
if (!entryExists(cachePath)) { | ||
// Must have the repo cloned in order to list its tags | ||
await _clone(_getUrl(id), cachePath); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same logic as in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 3d9c0ab |
||
|
||
List results = await git.run(["tag", "-l"], workingDir: cachePath); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you pass a pattern to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in computmaxer@59f2e6f |
||
List<Pubspec> validVersions = []; | ||
for (String version in results) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would require an additional loop - the I sort of prefer it as one loop, but if you would rather have that separation I can change it. |
||
// Strip preceding 'v' character so 'v1.0.0' can be parsed into a Version | ||
if (version.startsWith('v')) { | ||
version = version.substring(1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should do some de-duplication to ensure that, if a version exists both with and without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed here: c935354 |
||
} | ||
try { | ||
// Use Version.parse to determine valid version tags | ||
Version validVersion = new Version.parse(version); | ||
// Fetch the pubspec for this version | ||
Pubspec pubspec = await _getPubspec(cachePath, validVersion.toString()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 8492ba2 |
||
if (pubspec != null) { | ||
validVersions.add(pubspec); | ||
} | ||
} on FormatException {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should definitely catch format exceptions from the version parse, but I don't think we should catch errors caused by an invalid pubpsec. It's a pretty safe assumption that once we're here, the user intentionally opted into version resolution over a given repo, and having some versions silently not appear may be confusing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider the case where a repo initially was tagged 1.0.0 without a pubspec (perhaps it used to be in JS or something ¯_(ツ)_/¯ ). But all tags after 2.0.0 have a pubspec. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think with my refactor, we'll be able to avoid trying to load pubspecs until they're actually required by the version solver, which should mitigate this. |
||
} | ||
|
||
if (validVersions.isNotEmpty) { | ||
return validVersions; | ||
} | ||
|
||
// No valid version tags were found, defer to super | ||
return super.getVersions(name, description); | ||
} | ||
|
||
/// Since we don't have an easy way to read from a remote Git repo, this | ||
/// just installs [id] into the system cache, then describes it from there. | ||
Future<Pubspec> describeUncached(PackageId id) { | ||
|
@@ -104,6 +148,7 @@ class GitSource extends CachedSource { | |
var parsed = new Map.from(description); | ||
parsed.remove('url'); | ||
parsed.remove('ref'); | ||
parsed.remove('use_version_tags'); | ||
if (fromLockFile) parsed.remove('resolved-ref'); | ||
|
||
if (!parsed.isEmpty) { | ||
|
@@ -149,6 +194,10 @@ class GitSource extends CachedSource { | |
Future<PackageId> resolveId(PackageId id) { | ||
return _ensureRevision(id).then((revision) { | ||
var description = {'url': _getUrl(id), 'ref': _getRef(id)}; | ||
bool useVersionTags = _useVersionTags(id); | ||
if (useVersionTags) { | ||
description['use_version_tags'] = useVersionTags; | ||
} | ||
description['resolved-ref'] = revision; | ||
return new PackageId(id.name, name, id.version, description); | ||
}); | ||
|
@@ -253,9 +302,22 @@ class GitSource extends CachedSource { | |
/// by [id] on the effective ref of [id]. | ||
/// | ||
/// This assumes that the canonical clone already exists. | ||
Future<String> _getRev(PackageId id) { | ||
return git.run(["rev-list", "--max-count=1", _getEffectiveRef(id)], | ||
workingDir: _repoCachePath(id)).then((result) => result.first); | ||
Future<String> _getRev(PackageId id) async { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're changing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 01e6787 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for linking to the commit w/ the fix. Makes these reviews so much better. 😄 |
||
var ref = _getEffectiveRef(id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider making this method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Future<String> _getRev(PackageId id) {
var description = _getDescription(id);
// If the id is already resolved, there's no need to rev-list to look for it.
var resolved = description['resolved-ref'];
if (description is Map && resolved != null) return resolved;
// If [id] includes a version and not a ref, try "rev-list $version" and
// "rev-list v$version". Otherwise, try "rev-list ${_getRef(id) ?? 'HEAD'}".
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm having an issue with this. Here's what the code roughly looks like: Future<String> _getRev(PackageId id) {
print('_getRev called: ${id.toString()}');
...
var ref = description is Map ? description['ref'] : null;
if (ref == null && id.version != null && id.version != Version.none) {
print('Trying to use version: ${id.version.toString()}');
// return the rev-list $version result
} else {
print('Returning _getRef(id) or HEAD');
// return rev-list _getRef(id) ?? 'HEAD';
}
} For some reason
That's with a description of If I leverage I will keep looking for a solution, but let me know if you have any ideas that will solve this. Then I should be able to get rid of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
At this point So this is why Open to suggestions :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I forgot that The underlying issue is that Git IDs are no longer context-independent in the way they used to be; we don't know whether one was created due to new-style version resolution or old-style looking at This probably calls for a full refactor of |
||
try { | ||
var result = await git.run(["rev-list", "--max-count=1", ref], | ||
workingDir: _repoCachePath(id)); | ||
return result.first; | ||
} on git.GitException { | ||
if (ref == id.version.toString()) { | ||
// Try again with a "v" before the ref in case this was a version tag | ||
ref = 'v$ref'; | ||
var result = await git.run(["rev-list", "--max-count=1", ref], | ||
workingDir: _repoCachePath(id)); | ||
return result.first; | ||
} | ||
rethrow; | ||
} | ||
} | ||
|
||
/// Clones the repo at the URI [from] to the path [to] on the local | ||
|
@@ -284,9 +346,28 @@ class GitSource extends CachedSource { | |
} | ||
|
||
/// Checks out the reference [ref] in [repoPath]. | ||
Future _checkOut(String repoPath, String ref) { | ||
return git.run(["checkout", ref], workingDir: repoPath).then( | ||
(result) => null); | ||
Future _checkOut(String repoPath, String ref) async { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that you won't need to modify this if you change |
||
try { | ||
await git.run(["checkout", ref], workingDir: repoPath); | ||
} on git.GitException { | ||
// Try again with a "v" before the ref in case this was a version tag | ||
await git.run(["checkout", 'v$ref'], workingDir: repoPath); | ||
} | ||
} | ||
|
||
/// Use `git show` to get the pubspec.yaml at a particular ref, | ||
/// then parse it into a Pubspec object | ||
/// | ||
/// It is possible that a pubspec didn't always exist, return null if | ||
/// that is the case. | ||
Future<Pubspec> _getPubspec(String repoPath, String ref) async { | ||
try { | ||
var result = await git.run(['show', '$ref:pubspec.yaml'], | ||
workingDir: repoPath); | ||
return new Pubspec.parse(result.join('\n'), systemCache.sources); | ||
} on git.GitException { | ||
return null; | ||
} | ||
} | ||
|
||
/// Returns the path to the canonical clone of the repository referred to by | ||
|
@@ -313,13 +394,17 @@ class GitSource extends CachedSource { | |
/// [resolveId]. | ||
/// | ||
/// [description] may be a description or a [PackageId]. | ||
String _getEffectiveRef(description) { | ||
description = _getDescription(description); | ||
String _getEffectiveRef(PackageId id) { | ||
Map description = _getDescription(id); | ||
if (description is Map && description.containsKey('resolved-ref')) { | ||
return description['resolved-ref']; | ||
} | ||
|
||
var ref = _getRef(description); | ||
if (_useVersionTags(description) && id.version != null && | ||
id.version != Version.none) { | ||
return id.version.toString(); | ||
} | ||
return ref == null ? 'HEAD' : ref; | ||
} | ||
|
||
|
@@ -338,4 +423,14 @@ class GitSource extends CachedSource { | |
if (description is PackageId) return description.description; | ||
return description; | ||
} | ||
|
||
/// Returns value of "use_version_tags" in the description | ||
/// | ||
/// [description] may be a description or a [PackageId]. | ||
bool _useVersionTags(description) { | ||
description = _getDescription(description); | ||
if (description is String) return false; | ||
return description.containsKey('use_version_tags') | ||
? description['use_version_tags'] : false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really not a fan of requiring users to explicitly annotate to get this behavior. For the most part, I don't expect people to be using version declarations for Git dependencies at all unless they want some sort of resolution, so requiring them to write more stuff seems annoying. And now that we're using
git show
, it shouldn't be too bad to get all the pubspecs.On the other hand, we should short-circuit if the description doesn't have a
version
listed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue I'm having here is that I can't seem to access the
version
in this method, as it is not part of the description.Any idea how to access
version
so I can short-circuit based on it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be possible to access
version
if it was in the description undergit
, like you initially had it, right?Is there any reason that version can't be under
git
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version solver (which is abstracted from the package source) won't respect it when it is under
git
. So you'd have include the sameversion
entry twice.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes, good point. I'd just change
Source.getVersions()
to take aPackageRef
rather than a name and description, and check whether the version constraint isany
or not.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean
PackageDep
instead ofPackageRef
?PackageRef
does not have aVersionConstraint
available.