Package.resolved should go in the .gitignore

title says it all...

6 Likes

This post is a big vague. Are you proposing Package.resolved should be in .gitignore when you run swift package init? Or, do you want to have a general discussion on the topic?

Package.resolved should be in the default .gitignore. it gets generated every time you run swift build and there’s no reason it should go in version control

1 Like

There are arguments for both keeping it in source control and ignoring it. It mainly depends on your use case. For e.g., a team may want to ensure everyone is using the same set of dependencies. Users should decide for themselves what they want to do with the file. I think not ignoring the file is the right default to be on the safer side of things. In future, I hope we have a custom template feature so you can use your own template with swift package init.

11 Likes

if you can edit the file it shouldn’t be generated then. at least not on any invocation of swift build. you delete your custom Package.resolved and then all of a sudden, a new one is there, but it’s not the one you originally had

Package.resolved file records the result of dependency resolution whenever SwiftPM performs it. If the file is already present, SwiftPM will try to resolve to the same versions as recorded in the file. The file is not meant to be edited by hand. If you delete the file, the next swift build invocation will basically become swift package update as it doesn't know the result of last resolution. You can read more about the behavior and motivation in this proposal.

2 Likes

how should this file be edited?

As I said, the file records the result of the last dependency resolution result. The file is managed by the package manager. A snippet from the proposal:

The version of every resolved dependency will be recorded in a Package.resolved file in the top-level package, and when this file is present in the top-level package it will be used when performing dependency resolution, rather than the package manager finding the latest eligible version of each package. swift package update will update all dependencies to the latest eligible versions and update the Package.resolved file accordingly.

Resolved versions will always be recorded by the package manager. Some users may chose to add the Package.resolved file to their package's .gitignore file. When this file is checked in, it allows a team to coordinate on what versions of the dependencies they should use. If this file is gitignored, each user will separately choose when to get new versions based on when they run the swift package update command, and new users will start with the latest eligible version of each dependency. Either way, for a package which is a dependency of other packages (e.g. a library package), that package's Package.resolved file will not have any effect on its client packages.

1 Like

gotcha. there should be a warning or prompt at least though whenever the package manager has to generate a new one

Sounds reasonable. Do you mind filing a JIRA for this?

done
https://bugs.swift.org/browse/SR-8348

Why? It plays the same role as .lock files from CocoaPods or Bundler, or Carthage’s .resolved. That means it should be predictable when it changes (only when updating dependencies), users shouldn’t really care about the contents (it’s just to keep everyone in sync), and it should never need to be updated by the user (it’s generated or edited only by SPM). So it seems like it should have all of the same behaviors, including not ignoring it and not warning when it changes (which will be visible in source control anyway).

5 Likes

Here and here is the relevant discussion for Bundler (Ruby's package manager) and here the same discussion for Yarn (Javascript's alternative package manager).

tl;dr: Most people agree that version lock files should always be committed in the case of applications; in the case of libraries it used to be that this was not always recommended, but by now this has changed and at least Bundler and yarn encourage developers to always check in the lock file.

6 Likes

At least bundler won't allow you to install if it finds that it can't satisfy the dependencies in the Gemfile.lock; if you use bundle exec, you won't even be able to run any kind of tool (server, console, test, etc.) if your dependencies don't match the specifications. Every update to the Gemfile.lock has to be triggered by some user action (e.g. bundle update, bundle install).

Four years later, what’s the position on that these days? Xcode Cloud seems to require Package.resolved to be checked in.

It really doesn't make sense to ignore the Package.resolved file.

If you want reproducible builds, there needs to be a recorded copy of the exact versions that your project was using at a point in time. Without a Package.resolved, you don't have that.

For example, if you have from: "1.5.0" in your Package.swift, but are ignoring Package.resolved, this might have been built for your users with version 1.5.1, but if you checkout and rebuild, it may automatically resolve version 1.5.8 (and what you're building no longer matches what you shipped to your users).

It can be worse if you're working on a team: you could have developers with quite varied resolved dependency versions based on when they last updated their packages.

I'd recommend that you check the Package.resolved into your repository so that it is tagged alongside releases.

5 Likes

As a counterpoint, if your executable package E and a library package B that E depends on both depend on another library package C (both by tracking a specific branch of C), Xcode would prefer the older commit for C from B's Package.resolved when updating E's dependencies. This was true around Swift 5.3/5.4, and may still be true.

During the 5.4 release period, I switched to ignoring Package.resolved in nested libraries that I control, but checking in Package.resolved for the executable package's repository. This has worked well for me as a solo developer building server & CLI tools, and has saved me countless hours of updating & checking-in Package.resolved in intermediate packages for an update to a deeply nested package dependency.

That's not really a counterpoint - you're still checking in the Package.resolved in the places that matter to your use case. So long as you don't have other consumers of those intermediary library packages, then really it's only the final consumer that matters in that tree of dependencies. If that's the case, you might even consider hosting them all in one package/repository to really ease development?

Fair. I was trying to illustrate a middle path of "check in where it counts." Counterpoint was probably the wrong word. A package graph depending on fewer, larger packages would be easier to keep in sync, but it feels more manageable long-term having more feature-focused packages. Strategically skipping checking in Package.resolved in a few libraries (for my purposes, anything that isn't an end-leaf executable) has helped reduce the burden of keeping that all in sync.

coming back to this 4 years later, i am firmly in the camp of “Package.resolved should be checked in”. in retrospect, most of my gripes with Package.resolved were actually problems with the swift package edit/unedit command.

unfortunately it is still extremely difficult to develop a project and its dependencies in tandem. i frequently find myself pushing up to a half-dozen git tags per day, since the SPM doesn’t allow you to depend on a dependency which itself depends on untagged commits.

i have yet to find a good workflow for this, other than “dumping everything into one repo” which seems to completely defeat the purpose of swift packages.

4 Likes