Open JohnSundell opened 7 years ago
@kareman @darthpelo @clayellis @pixyzehn @cojoj @alexaubry @garricn Would love any comments you might have on this π (Let me know if it's annoying that I ping you on Marathon issues/PRs btw π )
This looks very good to me.
Seems like a reasonable request. Similar to version pinning in a Podfile, right?
So marathon pin
will just append @{VERSION}
to the Marathonfile?
Can the user do this manually?
Can the user specify the pin when running marathon add
?
Should we use the pin for Package.swift
for marathon export
?
Great feedback π
No, it'll keep track of the pinned versions internally, probably as part of the package cache (each package has a Package
object that specifies its version, name, url). A Marathonfile
is different from a Podfile
in that it's not required for running scripts, it's just a way to specify dependencies for other people running your script.
Yeah, it's all manually done. It can either be done through the pin
command or by adding the version annotations to either a Marathonfile
or when specifying dependencies inline (next to the import
statement).
That's a good idea π We could add a --version
argument to add
, so that a package can be added and pinned directly, like marathon add https://github.com/johnsundell/unbox --version 2.2
.
Totally! π It's fine for the initial implementation of export
not to support pinning though, so it isn't blocked by this - it's something we can easily add later π
The lack of this feature is a dealbreaker for the use of Marathon. Without pinned dependencies, any script I write using Marathon can and will randomly break in the future when the dependency updates in a non-backwards-compatible way. This is especially true any time there's a major Swift update.
Not only do we need a way to pin, but the // marathon:url
comment syntax needs to support specifying that pinned version.
Hi @JohnSundell,
Do you have any updates on this issue?
this ticket needs some more help to push things along - maybe we need a rethink.
UPDATE
so pulling apart code it's apparent that there's another framework 'releases' that seems to be doing heavy lifting around getting the latest verison. https://github.com/JohnSundell/releases
@discardableResult public func addPackage(at url: URL, throwIfAlreadyAdded: Bool = true) throws -> Package {
let name = try nameOfPackage(at: url)
if throwIfAlreadyAdded {
guard (try? folder.file(named: name)) == nil else {
throw Error.packageAlreadyAdded(name)
}
}
let latestVersion = try latestMajorVersionForPackage(at: url)
let package = Package(name: name, url: absoluteRepositoryURL(from: url), majorVersion: latestVersion)
try save(package: package)
try updatePackages()
addMissingPackageFiles()
return package
}
private func latestMajorVersionForPackage(at url: URL) throws -> Int {
printer.reportProgress("Resolving latest major version for \(url.absoluteString)...")
let releases = try perform(Releases.versions(for: url).withoutPreReleases(),
orThrow: Error.failedToResolveLatestVersion(url))
guard let latestVersion = releases.sorted().last else {
throw Error.failedToResolveLatestVersion(url)
}
return latestVersion.major
}
internal final class AddTask: Task, Executable {
private typealias Error = AddError
// MARK: - Executable
func execute() throws {
guard let identifier = arguments.first else {
throw Error.missingIdentifier
}
guard let url = URL(string: identifier) else {
throw Error.invalidURL(identifier)
}
let package = try packageManager.addPackage(at: url)
printer.output("π¦ \(package.name) added")
}
}
we would just need to target an arbitrary release here releases.sorted().last
perhaps overload AddTask - arguments.first = git url from Marathonfile arguments.second -> git version number / tag / release
We have a Version class which will accommodate string -> Version https://github.com/JohnSundell/Releases/blob/master/Sources/Version.swift#L48
@discardableResult public func addPackage(at url: URL,version: Version?, throwIfAlreadyAdded: Bool = true) throws -> Package {
let name = try nameOfPackage(at: url)
if throwIfAlreadyAdded {
guard (try? folder.file(named: name)) == nil else {
throw Error.packageAlreadyAdded(name)
}
}
if let version = version{
let specificVersion = try specificVersionForPackage(at: url,version:Version)
let package = Package(name: name, url: absoluteRepositoryURL(from: url), majorVersion: latestVersion)
try save(package: package)
try updatePackages()
addMissingPackageFiles()
return package
}else{
let latestVersion = try latestMajorVersionForPackage(at: url)
let package = Package(name: name, url: absoluteRepositoryURL(from: url), majorVersion: latestVersion)
try save(package: package)
try updatePackages()
addMissingPackageFiles()
return package
}
}
In order to make it easier to work with & share scripts that have dependencies, we should add support for pinning a certain version of a package to a script. The Swift Package Manager already supports this feature, but using it in Marathon requires circumventing the global package cache that Marathon uses to speed things up (to not have to re-clone and re-build each package for all scripts).
For example, the user should be able to use version
1.0
of a given package in one script, and then version2.0
of that same package in another script, and doing this shouldn't affect the global package version.Changes required
Version pinning in a Marathonfile
Currently a
Marathonfile
simply consists of newline separated URLs, so we'll need to add a syntax to define what version of a given dependency that should be used. This syntax is proposed:Note that it shouldn't matter whether spaces are added around the
@
character, both should parse correctly.If the version suffix is omitted the behavior will be the same as today, the version that Marathon currently has in its package cache will be used.
Version pinning for inline dependencies
Similarly, we should allow for the same syntax to be used for inline dependency annotations, like this:
Pinning a version on the command line
We also need to support pinning a version to a script from the command line. The proposal here is to introduce a
pin
command that pins a given version of a package (that needs to already be added to Marathon) to a script at a given path, like this:We could also allow the script name to be omitted, if the current folder only contains a single script.
Unpinning a version on the command line
Finally, we need a way to "unpin" a version of a package from a script. This only needs command line support, and could either be done by adding a
--remove
argument to thepin
command, like this:But to be more clear and consistent with our other commands, the proposal is instead to add a dedicated
unpin
command:Like with the
pin
command, we could allow the script name to be omitted, if the current folder only contains a single script.