Proposal: Package Manager Version Pinning

I’m puzzled. If a package’s pinning does not affect any other package that uses it, why should the defaults be different? A library will still suffer from all the “works for me” problems an app might.

Is the rationale that not pinning libraries encourages accidental testing of new versions of a library’s dependencies as they arrive?

Yep.

Thanks everyone who participated in this discussion.

We took the feedback on this thread and went back and created a revised proposal, here:
  https://github.com/apple/swift-evolution/blob/master/proposals/0145-package-manager-version-pinning.md
which we are putting up for review today:
  [swift-evolution] [Review] SE-0145: Package Manager Version Pinning

Ultimately, we decided to not actually change any of the proposed behavior, *for now*, because we couldn't come to a consensus about how that should look.

What we *did* do is try and write up much more about (a) the current behavior, and (b) the motivation behind the decisions (for example, the implications of not being able to include multiple versions of the same package in one build).

What we also agreed on was that the main thing we need right now is to implement the *mechanisms* in the proposal, and we didn't want the debate over the policy decision (to create the pin file by default) to side track that. So we decided to continue with the proposal as is, but to also reevaluate the impact of the policy decision in the proposal, in time to adjust it for Swift 4 if we decide the way it has played out in the ecosystem so far isn't as intended.

We also agreed there was room for a workflow component around specifying the "style" of package being developed (final application versus something being primarily developed for use as a dependency), but we didn't want to block implementation of the mechanism on figuring out the right design for that. If we get a design for that, we will also probably use that to change the default policy here.

Please take a look at the new proposal, and see what you think.

Thanks,
- Daniel

···

On Oct 14, 2016, at 4:42 PM, Daniel Dunbar via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 14, 2016, at 4:02 PM, Paul Cantrell <cantrell@pobox.com> wrote:

Sorry for the late arrival to this thread. Comments below…

On Oct 14, 2016, at 3:09 PM, Daniel Dunbar via swift-build-dev <swift-build-dev@swift.org> wrote:

What this proposal about is in one sense being able to export and share those pins.

This is a crucial and clarifying insight. It should be in the proposal! Near the top.

Good idea, will incorporate it.

2. Huon, Alexis, and I all agree we should never *inherit* pins by default.

Indeed. Pins should be only be about sharing specific versions within a development team — not with client packages / apps. What’s pinned in Vegas stays in Vegas. Publishing pins to other projects would be nonsensical.

5. Given that many people agree there are two workflows (we ourselves had talked about this a lot when writing the proposal, but didn't put it in), we felt it makes sense to consider adding that as an explicit declaration *somewhere*.

@Eloy, @Orta: Suppose we had a semantic notion of which packages were intended to be "top-level" versus used as a dependency, and we chose our defaults accordingly (in this case, we would orient workflows towards pinning by default in the top-level case, in the used as a dependency case we would orient away from it, e.g. warning you if you checked it in). What would you think of such a design?

I’m puzzled. If a package’s pinning does not affect any other package that uses it, why should the defaults be different? A library will still suffer from all the “works for me” problems an app might.

Is the rationale that not pinning libraries encourages accidental testing of new versions of a library’s dependencies as they arrive? Or is there another rationale for having different defaults?

I'll defer to this comment (linked from someone else earlier in the thread), which happens to match up with my perspective:
Shrinkwrap file from added packages are ignored · Issue #838 · yarnpkg/yarn · GitHub

- Daniel

Cheers, P

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi,

Suppose we had a semantic notion of which packages were intended to be "top-level" versus used as a dependency, and we chose our defaults accordingly (in this case, we would orient workflows towards pinning by default in the top-level case, in the used as a dependency case we would orient away from it, e.g. warning you if you checked it in). What would you think of such a design?

What is wrong with checking in the versions of your dependencies even if you are not a top-level package?
That’s a great way to document which versions do work together and may be helpful for the maintainer of the top-level package which is depending on you (he can easily compare the version files to find problems).

I think we agree that `swift package` within the top-level project should not look at the Package version files of its dependencies.
So these files will not change anything for the top-level builds.

I really think that the easiest solution is to always store the versions of all dependencies in SCM.
Then have some `swift package update` to move all versions to the latest consistent set and we have everything we need.
Each package can be built reproducibly and can be managed independently.

If packages want to track their dependencies closely, then simply set up some CI which does the update automatically.
You could automatically create create ‚update‘-commits, test them and automatically merge them if you really want to.
But every update is documented and you can even bisect bugs which were introduced by the updates.
This is helpful both for top-level as well as for library projects.

— Martin

The other point is that when working in a multi-language environment, having conventions such as this broken causes additional mental burden. That is, after working with JavaScript/Rust/iOS with CocoaPods and then switching to Swift, would require a lot of unneeded context switching, as in: "Where's that lockfile? I can't find it. Oh great, turns out it's not a lockfile, it's something else".

In Swift, we have pretty consistently tried to choose the "right" answer to make the resulting language consistent and beautiful.

I'm perfectly happy to have a discussion about the naming, but I would like it to be driven by what we believe the "right" answer is, not simply by deference to existing solutions. I do sympathize with wanting to be consistent where there is little value in diverging, but most people I have discussed this with agree that "lock" is actually the *wrong* word to use for this operation.

Also, please be mindful that your perspective is biased by the tools you are familiar with. Python's pip tool, for example, uses "freeze" for this, and Heroku expects "requirements.txt", so someone coming from that ecosystem could make the same argument in another direction. I find arguments about "A does X so we should do X" most compelling when it comes with a relatively complete survey of existing tools. not just a particular slice of possible tools.

Whether "pinning" is the right word is a different debate, but when we view pinning as a workflow-focused feature, versus the specification in the manifest (which is the "requirement"), then I think the connotation actually works fairly well (e.g., a pinboard is something you pin to while working, or pinning a dress while you stitch it). I also wasn't a huge fan of pin initially, but as it bounced around in my head for a while I really started to like it, for exactly this connotation reason.

- Daniel

···

On Oct 14, 2016, at 10:03 AM, Max Desiatov via swift-evolution <swift-evolution@swift.org> wrote:

With best regards, Max.

On 14 Oct 2016, at 17:59, Max Desiatov <max.desiatov@gmail.com <mailto:max.desiatov@gmail.com>> wrote:

I also agree with the point that .lock extension better suits here and adheres to a convention already established in other languages. I personally would prefer the file to be named Package.lock, not Package.pins and also think that it would help newcomers who already used other package managers, especially CocoaPods.

With best regards, Max.

On 14 Oct 2016, at 17:43, Alexis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Oct 14, 2016, at 2:01 AM, Ankit Aggarwal via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

We're proposing version pinning feature in Swift Package Manager. The proposal is available here <https://github.com/aciidb0mb3r/swift-evolution/blob/version-pinning/proposals/NNNN-Version-Pinning.md&gt; and also in this email:

Feedback welcomed!

Thanks,
Ankit

--------

Package Manager Version Pinning
Proposal: SE-XXXX
Author: Daniel Dunbar <https://github.com/ddunbar&gt;, Ankit Aggarwal <https://github.com/aciidb0mb3r&gt;
Review Manager: TBD
Status: Discussion
Introduction
This is a proposal for adding package manager features to "pin" or "lock" package dependencies to particular versions.

Motivation
As used in this proposal, version pinning refers to the practice of controlling exactly which specific version of a dependency is selected by the dependency resolution algorithm, independent from the semantic versioning specification. Thus, it is a way of instructing the package manager to select a particular version from among all of the versions of a package which could be chosen while honoring the dependency constraints.

Terminology

We have chosen to use "pinning" to refer to this feature, over "lockfiles", since the term "lock" is already overloaded between POSIX file locks and locks in concurrent programming.

I’ve never seen this cause any actual confusion, nor has anyone I know who teaches/develops these sorts of tools. As far as I can tell, the broader programming community is rapidly converging on this as standard terminology:

* Gemfile.lock (Ruby)
* Cargo.lock (Rust)
* Composer.lock (PHP)
* yarn.lock (JS)
* pubspec.lock (Dart)
* Podfile.lock (Swift/Objc!)

Diverging from this seems counter-productive.

Philosophy

Our philosophy with regard to pinning is that we actively want to encourage packages to develop against the latest semantically appropriate versions of their dependencies, in order to foster rapid development amongst the ecosystem and strong reliance on the semantic versioning concept. Our design for version pinning is thus intended to be a feature for package authors and users to use in crafting specific workflows, not be a mechanism by which most of the packages in the ecosystem pin themselves to specific versions of each other.

Use Cases

Our proposal is designed to satisfy several different use cases for such a behavior:

Standardizing team workflows

When collaborating on a package, it can be valuable for team members (and continuous integration) to all know they are using the same exact version of dependencies, to avoid "works for me" situations.

This can be particularly important for certain kinds of open source projects which are actively being cloned by new users, and which want to have some measure of control around exactly which available version of a dependency is selected.

Difficult to test packages or dependencies

Complex packages which have dependencies which may be hard to test, or hard to analyze when they break, may choose to maintain careful control over what versions of their upstream dependencies they recommend -- even if conceptually they regularly update those recommendations following the true semantic version specification of the dependency.

Dependency locking w.r.t. deployment

When stabilizing a release for deployment, or building a version of a package for deployment, it is important to be able to lock down the exact versions of dependencies in use, so that the resulting product can be exactly recreated later if necessary.

Proposed solution
We will introduce support for an optional new file Package.pins adjacent to the Package.swift manifest, called the "pins file". We will also introduce a number of new commands (see below) for maintaining the pins file.

This file will record the active version pin information for the package, including data such as the package identifier, the pinned version, and explicit information on the pinned version (e.g., the commit hash/SHA for the resolved tag).

The exact file format is unspecified/implementation defined, however, in practice it will be a JSON data file.

This file may be checked into SCM by the user, so that its effects apply to all users of the package. However, it may also be maintained only locally (e.g., placed in the .gitignore file). We intend to leave it to package authors to decide which use case is best for their project.

In the presence of a Package.pins file, the package manager will respect the pinned dependencies recorded in the file whenever it needs to do dependency resolution (e.g., on the initial checkout or when updating).

The pins file will not override Manifest specified version requirements and it will be an error (with proper diagnostics) if there is a conflict between the pins and the manifest specification.

Detailed Design
We will add a new command pin to swift package tool with following semantics:

$ swift package pin ( [--all] | [<package-name>] [<version>] ) [--message <message>]
The package-name refers to the name of the package as specified in its manifest.

This command pins one or all dependencies. The command which pins a single version can optionally take a specific version to pin to, if unspecified (or with --all) the behaviour is to pin to the current package version in use. Examples:

$ swift package pin --all - pins all the dependencies.
$ swift package pin Foo - pins Foo at current resolved version.
$ swift package pin Foo 1.2.3 - pins Foo at 1.2.3. The specified version should be valid and resolvable.
The --reason option is an optional argument to document the reason for pinning a dependency. This could be helpful for user to later remember why a dependency was pinned. Example:

$ swift package pin Foo --reason "The patch updates for Foo are really unstable and need screening."
Dependencies are never automatically pinned, pinning is only ever taken as a result of an explicit user action.

We will add a new command unpin:

$ swift package unpin ( [--all] | [<package-name>] )
This is the counterpart to the pin command, and unpins one or all packages.

We will fetch and resolve the dependencies when running the pin commands, in case we don't have the complete dependency graph yet.

We will extend the workflow for update to honour version pinning, that is, it will only update packages which are unpinned, and it will only update to versions which can satisfy the existing pins. The update command will, however, also take an optional argument --repin:

$ swift package update [--repin]
Update command errors if there are no unpinned packages which can be updated.

Otherwise, the behaviour is to update all unpinned packages to the latest possible versions which can be resolved while respecting the existing pins.

The [--repin] argument can be used to lift the version pinning restrictions. In this case, the behaviour is that all packages are updated, and packages which were previously pinned are then repinned to the latest resolved versions.

The update and checkout will both emit logs, notifying the user that pinning is in effect.

The swift package show-dependencies subcommand will be updated to indicate if a dependency is pinned.

As a future extension, we anticipate using the SHA information recorded in a pins file as a security feature, to prevent man-in-the-middle attacks on parts of the package graph.

Impact on existing code
There will be change in the behaviours of swift build and swift package update in presence of the pins file, as noted in the proposal however the existing package will continue to build without any modifications.

Alternative considered
We considered making the pinning behavior default on running swift build, however we think that pinning by default is likely to make the package graph more constrained than it should be. It drives the user away from taking full advantage of semantic versioning. We think it will be good for the package ecosystem if such a restriction is not the default behavior and that this design will lead to faster discovery of bugs and fixes in the upstream.

I agree with the others that this is the better solution.

With regards to the constraining problem, the key insight adopted by Cargo/Yarn/Bundler is to distinguish libraries from applications. A library shouldn’t pin its dependencies, while an application should. This ensures that the ecosystem itself is maximally unconstrained, while ensuring actual applications continue to reliably build, regardless of ecosystem changes and the computer that it was built on. If a version of a library has trouble building with different versions, it should ideally specify that with its dependency constraints, not a lockfile.

This also ensures that there’s diverse testing of versions: CI for applications will verify particular configurations, while CI for libraries will verify the latest-and-greatest works.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree with everything here. :)

- Daniel

···

On Oct 14, 2016, at 12:04 PM, Ryan Lovelett <swift-dev@ryan.lovelett.me> wrote:

Whether "pinning" is the right word is a different debate, but when we view pinning as a workflow-focused feature, versus the specification in the manifest (which is the "requirement"), then I think the connotation actually works fairly well (e.g., a pinboard is something you pin to while working, or pinning a dress while you stitch it). I also wasn't a huge fan of pin initially, but as it bounced around in my head for a while I really started to like it, for exactly this connotation reason.

I think this comment drives at the core of the difference between this proposal and what Orta, Alexis and others are saying. (At least for me)

The description you've provided here, specifically the word "workflow", and the concept of operations described by the proprosal are meant to be a transient. They are meant as a temporary description of how these dependencies should be kept in order. To my mind one "puts a pin" in something temporarily. To comeback to it later to do something with it at that time. Thus the pin probably does work with this concept.

Conversely, the concept of "locking" something feels less transient. It feels more permanent. More like the concept of operations described by Orta, Alexis and others. Idle speculation: might be why those other managers selected the word lock.

Not sure where I come down on the whole thing yet. Just wanted to say that if the propsal goes another way than what was first propsed a new word likely should be investigated.

Ok, great! We will discuss this some more and see what we can come up with.

- Daniel

···

On Oct 14, 2016, at 2:18 PM, orta therox <orta.therox@gmail.com> wrote:

Same, yeah :+1:

On 14 Oct 2016, at 21:21, Eloy Durán via swift-build-dev <swift-build-dev@swift.org> wrote:

5. Given that many people agree there are two workflows (we ourselves had talked about this a lot when writing the proposal, but didn't put it in), we felt it makes sense to consider adding that as an explicit declaration *somewhere*.

@Eloy, @Orta: Suppose we had a semantic notion of which packages were intended to be "top-level" versus used as a dependency, and we chose our defaults accordingly (in this case, we would orient workflows towards pinning by default in the top-level case, in the used as a dependency case we would orient away from it, e.g. warning you if you checked it in). What would you think of such a design?

Oooooooh, I like it.

Even though I would probably still pin/lock personally, I think this default combined with the explicit declaration strikes a perfect balance in trade-offs :+1:
_______________________________________________
swift-build-dev mailing list
swift-build-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-build-dev

Whether "pinning" is the right word is a different debate, but when we
view pinning as a workflow-focused feature, versus the specification
in the manifest (which is the "requirement"), then I think the
connotation actually works fairly well (e.g., a pinboard is something
you pin to while working, or pinning a dress while you stitch it). I
also wasn't a huge fan of pin initially, but as it bounced around in
my head for a while I really started to like it, for exactly this
connotation reason.

I think this comment drives at the core of the difference between this
proposal and what Orta, Alexis and others are saying. (At least for me)

The description you've provided here, specifically the word "workflow",
and the concept of operations described by the proprosal are meant to be
a transient. They are meant as a temporary description of how these
dependencies should be kept in order. To my mind one "puts a pin" in
something temporarily. To comeback to it later to do something with it
at that time. Thus the pin probably does work with this concept.

Conversely, the concept of "locking" something feels less transient. It
feels more permanent. More like the concept of operations described by
Orta, Alexis and others. Idle speculation: might be why those other
managers selected the word lock.

Not sure where I come down on the whole thing yet. Just wanted to say
that if the propsal goes another way than what was first propsed a new
word likely should be investigated.

Hey,

[...]
I see it as my responsibility to know exactly what code I’m pulling into my package. In my view, it’s absolutely unsafe to trust other people’s code. Even when they mean no harm, trusting them to properly apply SemVer is the same issue.

maybe we should have the tooling support that? Elm does try to enforce correct semantic versioning. Maybe swift-pm should do that too?

See http://elm-lang.org :
<quote>
Enforced Semantic Versioning

Elm can detect all API changes automatically thanks to its type system. We use that information to force everything in our package catalog to follow semantic versioning precisely. No more surprises in PATCH releases!
</quote>

I have no idea how well it works but if we'll end up relying on proper semantic versioning, tool support sounds like a good idea to me.

[...]

Cheers,
  Johannes

Hey,

[...]
I see it as my responsibility to know exactly what code I’m pulling into my package. In my view, it’s absolutely unsafe to trust other people’s code. Even when they mean no harm, trusting them to properly apply SemVer is the same issue.

maybe we should have the tooling support that? Elm does try to enforce correct semantic versioning. Maybe swift-pm should do that too?

We would like to (try to), and it is on the long list of ideal future things to do. It requires a lot of compiler support no one has signed up for yet, though...

- Daniel

···

On Oct 14, 2016, at 4:15 PM, Johannes Weiß <johannesweiss@apple.com> wrote:

See http://elm-lang.org :
<quote>
Enforced Semantic Versioning

Elm can detect all API changes automatically thanks to its type system. We use that information to force everything in our package catalog to follow semantic versioning precisely. No more surprises in PATCH releases!
</quote>

I have no idea how well it works but if we'll end up relying on proper semantic versioning, tool support sounds like a good idea to me.

[...]

Cheers,
Johannes

I’m puzzled. If a package’s pinning does not affect any other package that uses it, why should the defaults be different? A library will still suffer from all the “works for me” problems an app might.

Is the rationale that not pinning libraries encourages accidental testing of new versions of a library’s dependencies as they arrive?

Yep.

I’m skeptical of whether disabling pinning by default is the right answer. It seems … haphazard and incomplete, likely to confuse those who don’t understand this reasoning, and only incidentally helpful to those who do.

I’d prefer an approach that is more mindful, more intentional.

Emitting warnings when newer versions of packages are available would help address the same issue. This would also encourage apps to get security patches promptly.

A more ambitious approach might be to provide a utility that installs and tests various combinations of all the semantically allowed versions of a lib’s dependencies — at the very least, all the lowest and highest allowed versions. This would ignore any existing pinfile. Library devs would run this prior to release, and on a CI server if they have one.

This would have the advantage of catching incompatibilities with _past_ versions of libraries as well as _new_ ones. In particular, it would catch too-permissive version specifications, e.g. if your library asks for foolib 3.x but relies on a bugfix in 3.4.2, this approach would catch that problem.

That’s clearly a bigger, separate idea, not necessary to hash out right now. I mean it just to illustrate what better approaches might look like. I’m skeptical that simply disabling pinning does a good job of solving the intended problem, and don’t think it should weigh quite so heavily on the proposal at hand.

The current idea wouldn't "disable it", it would discourage you from checking it in for a library (it really comes down to you have to run two commands, not one). I agree all the other things you outline are useful, and that not checking it in doesn't magically solve the problem here.

- Daniel

···

On Oct 14, 2016, at 4:51 PM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 14, 2016, at 6:34 PM, Eloy Durán <eloy.de.enige@gmail.com> wrote:

Cheers,

Paul

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.swift.org_mailman_listinfo_swift-2Devolution&d=CwIGaQ&c=Hw-EJUFt2_D9PK5csBJ29kRV40HqSDXWTLPyZ6W8u84&r=teZCCTj9bCvNJ4SX_xKQ4SOVlaMWK9IXMlx_HMapKUU&m=wl9y8ecLDwSW83LjsOje4Jz1_TjqamVYnv-lTnVooiI&s=ICIfe-G4A6ZgJyiYYtMnHpq6JKQpHA5nhxc8nG4GxhY&e=

I’m skeptical of whether disabling pinning by default is the right answer. It seems … haphazard and incomplete, likely to confuse those who don’t understand this reasoning, and only incidentally helpful to those who do.

I’d prefer an approach that is more mindful, more intentional.

Emitting warnings when newer versions of packages are available would help address the same issue. This would also encourage apps to get security patches promptly.

A more ambitious approach might be to provide a utility that installs and tests various combinations of all the semantically allowed versions of a lib’s dependencies — at the very least, all the lowest and highest allowed versions. This would ignore any existing pinfile. Library devs would run this prior to release, and on a CI server if they have one.

This would have the advantage of catching incompatibilities with _past_ versions of libraries as well as _new_ ones. In particular, it would catch too-permissive version specifications, e.g. if your library asks for foolib 3.x but relies on a bugfix in 3.4.2, this approach would catch that problem.

That’s clearly a bigger, separate idea, not necessary to hash out right now. I mean it just to illustrate what better approaches might look like. I’m skeptical that simply disabling pinning does a good job of solving the intended problem, and don’t think it should weigh quite so heavily on the proposal at hand.

Cheers,

Paul

···

On Oct 14, 2016, at 6:34 PM, Eloy Durán <eloy.de.enige@gmail.com> wrote:

I’m puzzled. If a package’s pinning does not affect any other package that uses it, why should the defaults be different? A library will still suffer from all the “works for me” problems an app might.

Is the rationale that not pinning libraries encourages accidental testing of new versions of a library’s dependencies as they arrive?

Yep.

That’s clearly a bigger, separate idea, not necessary to hash out right now. I mean it just to illustrate what better approaches might look like. I’m skeptical that simply disabling pinning does a good job of solving the intended problem, and don’t think it should weigh quite so heavily on the proposal at hand.

The current idea wouldn't "disable it”

Right, my bad wording. Is “turn off / discourage pinfiles by default for libraries but not apps” a better description of the general idea?

it would discourage you from checking it in for a library (it really comes down to you have to run two commands, not one). I agree all the other things you outline are useful, and that not checking it in doesn't magically solve the problem here.

If the difference were only what’s in .gitignore, I’d be completely comfortable with that. Enthusiastic.

And if there’s some distinction between libs and top-level packages that only affects a generated .gitignore and/or emitted warnings, I’d be completely comfortable with that.

I’m skeptical of deeper special-casing for libs vs. top-level, but it sounds like the special-casing may not actually be that deep. If so, I’m just fussing over nothing!

···

On Oct 14, 2016, at 7:18 PM, Daniel Dunbar <daniel_dunbar@apple.com> wrote:

On Oct 14, 2016, at 4:51 PM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 14, 2016, at 7:17 PM, Alexis Beingessner <abeingessner@apple.com> wrote:

A few comments down, Yehuda even provides an example of him doing just that with Bundler:

Shrinkwrap file from added packages are ignored · Issue #838 · yarnpkg/yarn · GitHub

Yeah, I’d love to see SwiftPM or a companion tool automate that. Totally tractable problem, and I’ll bet we see quality payoffs in the lib ecosystem.

Cheers,

Paul

Same, yeah :+1:

···

On 14 Oct 2016, at 21:21, Eloy Durán via swift-build-dev <swift-build-dev@swift.org> wrote:

5. Given that many people agree there are two workflows (we ourselves had talked about this a lot when writing the proposal, but didn't put it in), we felt it makes sense to consider adding that as an explicit declaration *somewhere*.

@Eloy, @Orta: Suppose we had a semantic notion of which packages were intended to be "top-level" versus used as a dependency, and we chose our defaults accordingly (in this case, we would orient workflows towards pinning by default in the top-level case, in the used as a dependency case we would orient away from it, e.g. warning you if you checked it in). What would you think of such a design?

Oooooooh, I like it.

Even though I would probably still pin/lock personally, I think this default combined with the explicit declaration strikes a perfect balance in trade-offs :+1:
_______________________________________________
swift-build-dev mailing list
swift-build-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-build-dev

[...]
I see it as my responsibility to know exactly what code I’m pulling into my package. In my view, it’s absolutely unsafe to trust other people’s code. Even when they mean no harm, trusting them to properly apply SemVer is the same issue.

maybe we should have the tooling support that? Elm does try to enforce correct semantic versioning. Maybe swift-pm should do that too?

See http://elm-lang.org :
<quote>
Enforced Semantic Versioning

Elm can detect all API changes automatically thanks to its type system. We use that information to force everything in our package catalog to follow semantic versioning precisely. No more surprises in PATCH releases!
</quote>

I have no idea how well it works but if we'll end up relying on proper semantic versioning, tool support sounds like a good idea to me.

This is what I was referring to when I mentioned that automation can only take you so far. It is easily possible to do a patch release where the API might not change, but the semantics of the code does.

In my opinion it requires human judgement to determine if a change is really something you can trust. Trusting SemVer for that is going to lead to problems and making people think that they can is just misleading in my book.

Not saying you can’t have tools to help guide choosing versions, though.

Hi :)

Daniel, it's all right, "There are only two hard things in Computer Science: cache invalidation and naming things.“

That’s true :-).

So I throw another name into the ring: „Package.versions“.

I think the file should be named after what it contains: the versions of all dependencies.
This is used to get reproducible builds by always using the same pinned version.
But that is how the file is _used_, not what the file _is_.

— Martin

Hi Eloy,

> [...]

I have no idea how well it works but if we'll end up relying on proper semantic versioning, tool support sounds like a good idea to me.

This is what I was referring to when I mentioned that automation can only take you so far. It is easily possible to do a patch release where the API might not change, but the semantics of the code does.

In my opinion it requires human judgement to determine if a change is really something you can trust. Trusting SemVer for that is going to lead to problems and making people think that they can is just misleading in my book.

Not saying you can’t have tools to help guide choosing versions, though.

agreed. But I think that if (whether that's good or bad) we rely on semantic versioning, tool support can make that a lot easier.

Cheers,
  Johannes

Not saying you can’t have tools to help guide choosing versions, though.

agreed. But I think that if (whether that's good or bad) we rely on semantic versioning, tool support can make that a lot easier.

Aye, agreed.

In Swift, we have pretty consistently tried to choose the "right" answer to make the resulting language consistent and beautiful.
I'm perfectly happy to have a discussion about the naming, but I would like it to be driven by what we believe the "right" answer is, not simply by deference to existing solutions.

+100

Moreover, after your explanation, `lock` feels wrong to me too.

I like the core idea here, but I feel that it could potentially prevent
teams updating, through focus on other things and general
laziness/ignorance to what's going on with external dependencies. Although
this gives me a separate idea...

How about an additional feature that can be run manually if desired, or
more likely run as a daily/overnight job on CI. I'm not entirely sure what
the command would be or how it would work exactly, but the general idea
would be:

* Build a list of dependencies that can potentially be updated (from the
entire dependency graph).
* For each updatable dependency in the list {
    * Keep all the other pins as-they-were and fetch the updated dependency.
    * Build the project and run its tests.
    * Keep track of which ones worked and which ones failed.
}
* If there are updatable dependencies that build successfully, and all
(your project) tests still pass - this can to be reported to the
development team.

This way, teams could use pinning and automate checking for compatible
updates. Also package owners could potentially be notified (with explicit
consent of their users) if they broke compatibility in what should have
been a compatible release. I realise that not everything going on here is
inside the remit of a package manager, but the package manager could
provide functionality to help with such things, a bit like git-bisect. Then
again, the SPM community proposal seems to cast quite a wide potential
remit.

An additional cool feature would be some mechanism to allow notifications
to the package owner if what they have released as compatible (according to
the version) actually isn't. Explicit opt-in from users for this one of
course, with options for what information to include. Maybe swift.org could
host this for package owners, and usage stats for them too?

In Swift, we have pretty consistently tried to choose the "right" answer

to make the resulting language consistent and beautiful.

I'm perfectly happy to have a discussion about the naming, but I would

like it to be driven by what we believe the "right" answer is, not simply
by deference to existing solutions.

+100

Moreover, after your explanation, `lock` feels wrong to me too.

···

On Sun, 16 Oct 2016 at 18:06 Georgios Moschovitis via swift-evolution < swift-evolution@swift.org> wrote:
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi Jay,

I like the core idea here, but I feel that it could potentially prevent teams updating, through focus on other things and general laziness/ignorance to what's going on with external dependencies. Although this gives me a separate idea...

I hope to tackle this through features which will help notify you when new versions you could upgrade to are available. I hope the default practice in the ecosystem will then become to heed those notifications and update.

How about an additional feature that can be run manually if desired, or more likely run as a daily/overnight job on CI. I'm not entirely sure what the command would be or how it would work exactly, but the general idea would be:

* Build a list of dependencies that can potentially be updated (from the entire dependency graph).
* For each updatable dependency in the list {
    * Keep all the other pins as-they-were and fetch the updated dependency.
    * Build the project and run its tests.
    * Keep track of which ones worked and which ones failed.
}
* If there are updatable dependencies that build successfully, and all (your project) tests still pass - this can to be reported to the development team.

This way, teams could use pinning and automate checking for compatible updates. Also package owners could potentially be notified (with explicit consent of their users) if they broke compatibility in what should have been a compatible release. I realise that not everything going on here is inside the remit of a package manager, but the package manager could provide functionality to help with such things, a bit like git-bisect. Then again, the SPM community proposal seems to cast quite a wide potential remit.

An additional cool feature would be some mechanism to allow notifications to the package owner if what they have released as compatible (according to the version) actually isn't. Explicit opt-in from users for this one of course, with options for what information to include. Maybe swift.org <http://swift.org/&gt; could host this for package owners, and usage stats for them too?

Both of the features you propose make a lot of sense to me, and are things I definitely would like to see us do in time.

For now, though, I see this feature as a "building block" towards those directions. Others can build on top of this feature to create automated systems like the ones you describe, I don't think we need to try and tackle everything ourselves in the initial proposal.

- Daniel

···

On Oct 16, 2016, at 7:14 PM, Jay Abbott <jay@abbott.me.uk> wrote:

On Sun, 16 Oct 2016 at 18:06 Georgios Moschovitis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> In Swift, we have pretty consistently tried to choose the "right" answer to make the resulting language consistent and beautiful.
> I'm perfectly happy to have a discussion about the naming, but I would like it to be driven by what we believe the "right" answer is, not simply by deference to existing solutions.

+100

Moreover, after your explanation, `lock` feels wrong to me too.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution