-
Notifications
You must be signed in to change notification settings - Fork 1.4k
improve correctness by using identity more broadly throughout the code #3244
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
Conversation
eea65aa
to
ad5a33c
Compare
@swift-ci please smoke test |
import TSCBasic | ||
import PackageModel | ||
|
||
// TODO: refactor this when adding registry support |
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.
👀
let isBaseURLRemote = URL.scheme(packageLocation) != nil | ||
|
||
func fixLocation(_ location: String, requirement: Requirement) throws -> String { | ||
fileprivate init( |
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.
👀 this is the key change, the logic to address identity resolution d location remapping (aka mirrors) moved to the constructor of the PackageDependencyDescription
which is the earliest time and makes the rest of the code simpler can it assume the dependency identity and location is correct
@@ -11,15 +11,33 @@ | |||
import TSCBasic | |||
|
|||
/// Represents a package dependency. | |||
public struct PackageDependencyDescription: Equatable, Codable, Hashable { | |||
public enum PackageDependencyDescription: Equatable { |
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.
👀 this is the other key change: PackageDependencyDescription
is now and enum with two cases: local
and git
. the idea is to model the data more accurately instead of the a struct that has fields that are reused with different meaning and make reasoning in downstream harder. As we get closer with the registry, we will add a 3rd option registry
with its own payload.
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 one drawback (since enums can't have stored properties) is that common properties have to be replicated across the payloads. I don't think this is a problem but another way that might make sense would be to separate out the reference type as an enum (path, git repo, registry), which would also make clear what is common to all packages (e.g. identity and filter) but leaving the specifics of the reference type to the enum. Just a thought.
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.
yes, this is the tradeoff here. i went with creating computed properties for the shared properties since its not a big price to pay, and the price is paid only once in PackageDependencyDescription itself, keeping the call-sites nice and clean. if we split the properties across the associated values and and some "type" enum, the call-sites would have to know how to access the properties so the code in PackageDependencyDescription would be cleaner but the call-sites "uglier"
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.
Agreed. Overall it's a good tradeoff. I expect it to be very rare to have to add enum cases here.
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.
my thoughts exactly
public let name: String | ||
// FIXME: we should simplify target based dependencies such that this is no longer required | ||
// A name to be used *only* for target dependencies resolution | ||
public var nameForTargetDependencyResolutionOnly: String { |
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.
renamed this so its obvious what its only fo
|
||
/// The dependency requirement. | ||
public enum Requirement: Equatable, Hashable { | ||
case exact(Version) | ||
case range(Range<Version>) | ||
case revision(String) | ||
case branch(String) | ||
case localPackage |
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.
a nice side effect of the move to enum is that we can get rid of localPackage
as kind of requirement which seems unnatural
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.
Agreed, that was always odd.
@@ -1309,6 +1309,7 @@ extension Workspace { | |||
/// This will load the manifests for the root package as well as all the | |||
/// current dependencies from the working checkouts. | |||
// @testable internal | |||
// FIXME: this logic should be changed to use identity instead of location once identity is unique |
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.
this is the next step in the identity cleanup journey, but before its unique we need to keep using locations here :(
@@ -65,4 +65,62 @@ class DependencyResolutionTests: XCTestCase { | |||
XCTAssertEqual(output, "♣︎K\n♣︎Q\n♣︎J\n♣︎10\n♣︎9\n♣︎8\n♣︎7\n♣︎6\n♣︎5\n♣︎4\n") | |||
} | |||
} | |||
|
|||
func testMirrors() { |
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.
😅
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.
Hmm, was there no test at all before?!
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.
there was a test that uses all mocks, no end-to-end test
@swift-ci please smoke test |
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.
This is great! Just had a question/suggestion for PackageDependencyDescription but this looks good.
public protocol IdentityResolver { | ||
func resolveIdentity(for location: String) -> PackageIdentity | ||
func resolveIdentity(for path: AbsolutePath) -> PackageIdentity | ||
func resolveLocation(from location: String) -> String |
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.
Is this String
because it could be either path or URL or identity?
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.
yes, I was contemplating moving to URL as well but wanted to minimize changes so kept String for now. We could/should revisiting this in follow-up PRs
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.
Great, that seems like a good choice (keeping PRs incremental)
// If the dependency URL starts with '~/', try to expand it. | ||
return fileSystem.homeDirectory.appending(RelativePath(String(dependencyLocation.dropFirst(2)))).pathString | ||
} else if dependencyLocation.hasPrefix(filePrefix) { | ||
// FIXME: SwiftPM can't handle file locations with file:// scheme so we need to |
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.
Right, in particular, file://
is treated as a remote package whose repo just happens to be in the local file system.
public struct Git: Equatable, Codable { | ||
public let identity: PackageIdentity | ||
public let name: String? | ||
public let location: String |
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.
Is this always a URL in this case? Would it make sense to use URL, or rather keep as String?
|
||
/// The dependency requirement. | ||
public enum Requirement: Equatable, Hashable { | ||
case exact(Version) | ||
case range(Range<Version>) | ||
case revision(String) | ||
case branch(String) | ||
case localPackage |
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.
Agreed, that was always odd.
@@ -11,15 +11,33 @@ | |||
import TSCBasic | |||
|
|||
/// Represents a package dependency. | |||
public struct PackageDependencyDescription: Equatable, Codable, Hashable { | |||
public enum PackageDependencyDescription: Equatable { |
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 one drawback (since enums can't have stored properties) is that common properties have to be replicated across the payloads. I don't think this is a problem but another way that might make sense would be to separate out the reference type as an enum (path, git repo, registry), which would also make clear what is common to all packages (e.g. identity and filter) but leaving the specifics of the reference type to the enum. Just a thought.
/// - SeeAlso: `explicitName` | ||
/// - SeeAlso: `url` | ||
public let name: String | ||
// FIXME: we should simplify target based dependencies such that this is no longer required |
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.
Agreed.
@@ -65,4 +65,62 @@ class DependencyResolutionTests: XCTestCase { | |||
XCTAssertEqual(output, "♣︎K\n♣︎Q\n♣︎J\n♣︎10\n♣︎9\n♣︎8\n♣︎7\n♣︎6\n♣︎5\n♣︎4\n") | |||
} | |||
} | |||
|
|||
func testMirrors() { |
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.
Hmm, was there no test at all before?!
05ca1e7
to
c613001
Compare
@swift-ci please smoke test |
c613001
to
5965575
Compare
@swift-ci please smoke test |
@neonichu lmk what you think |
5965575
to
9aef406
Compare
motivation: improve correctness by using identity more broadly throughout the code, prepare to package registry changes: * refector as PackageDependencyDescription with "scm" and "local" cases, this reflects better the true meaning and associated data with the two options and prepares us to adding a third option for registry * create new abstraction IdentityResolver, with default implementation that follows existing identity resolution logic * move mirror logic into IdentityResolver, and simplify code that used mirrors * move all logic to generate identity, apply mirrors to PackageDependencyDescription so it is handles as soon as possible in the call stack * adjust call-sites * adjust and add tests for mirror
9aef406
to
e77f4ae
Compare
@swift-ci please smoke test |
@neonichu renamed |
@swift-ci please smoke test macOS |
1 similar comment
@swift-ci please smoke test macOS |
motivation: improve correctness by using identity more broadly throughout the code, prepare to package registry
changes:
builds on #3239 and #3240 so lets merge those first