Open mc7h opened 3 years ago
Hi there,
The Renovate team needs your help! Before we can start work on your issue we first need to know exactly what's causing the current behavior. A minimal reproduction helps us with this.
To get started, please read our guide on minimal reproductions to understand what is needed.
We may close the issue if you have not provided a minimal reproduction within two weeks. If you need more time, or are stuck, please ask for help or more time in a comment.
Good luck,
The Renovate team
This sounds like something Renovate could do, but we'll need to understand the file formats and process better e.g.
project.pbxproj
is updated?Package.swift
and project.pbxproj
, if any?I think the real challenge will lie in the fact that there doesn't appear to be a Public specification for .pbxproj
so any solution would need to be based on observation alone and could be fragile. That said, there are Projects that work with the .pxproj
files successfully (e.g https://github.com/CocoaPods/Xcodeproj)
Package.resolved
file doesn't need to be refreshed every time the project.pbxproj
is updated; but it will be changed when a new Dependency is added (and resolved) or when Dependencies are updated through the Xcode UI.project.pbxproj
is a fairly-huge file. It describes almost every aspect of an Xcode project. For dependencies, rather than having the usual Package.swift
-> Package.resolved
relationship Apple has chosen to embed all thePackage.swift
content to the project.pbxproj
in the section: XCRemoteSwiftPackageReference
. From what I can gather, the Package.resolved
format does not differ from what we already have with Swift packages.I've started to pick this up. There's a couple nuances that we need to tackle, one of which is that the existing SPM solution doesn't handle the resolved file where it's a lot more necessary for Xcode (as it uses it as a source of truth and doesn't update automatically as frequently as a Swift Package framework).
Depending on the rangeStrategy, we can get away with either updating just the project file, or we need to update both the project file and the resolved file.
It's going to take a bit of messing to get it perfect and to support all permutations of package type.
Filling out the template for a new manager. Reproduction: https://github.com/JamesSherlouk/renovate-xcode/
Did you read our documentation on adding a package manager?
swift-xcode
(Swift Package Manager for Xcode)
I have also considered naming this simply
xcode
(as there is no other package manager native to Xcode) orswift
merging functionality with the existing manager.
Swift
According to the iOS Developer Survey ran in 2020 (source), Swift Package Manager is the leading package manager for newer and personal projects with CocoaPods taking a small lead in business applications. I believe it is fair to say that Swift Package Manager is constantly growing within the community, and due to it's first-party integrations and support will (if it hasn't already) overtake CocoaPods shortly.
Developers in the Apple ecosystem have several choices including CocoaPods (which Renovate supports), Carthage, and Swift Package Manager (SPM). SPM is the only first-party package manager supported by Apple for use with iOS and other Apple platforms.
Unlike the existing SPM support started in #3911, when included in Xcode projects there is no Package.swift
file. The dependency definitions are included in the Xcode project file which, thanks Apple, uses an undocumented structure.
The matched file should point to the Xcode project definition which which is called project.pbxproj
and sits within the project's .xcodeproj
directory. The default configuration includes a regex for this.
It is almost definitely true that this will pickup Xcode project files that do not specify Swift packages in them, though I wouldn't call this a "false hit" as they are genuine package definitions, it just happens that they don't have any.
The project file can include local references, but these are ignored by the manager as they're not linked as Swift packages which is what it looks for.
Nope, each project file can be parsed independently.
Apple uses a proprietary format which is undocumented. It may change at any point. There are a number of other libraries which do attempt to parse it, but I didn't believe them to be a good fit for this project as it would dramatically increase the complexity for the small part we need.
It is possible with a fair bit more work to identify which Xcode "target" each dependency is tied to. A dependency can be in one or more targets. Each target has a "product type" which does tell us whether it's designed for testing, or is for the application. By our definition, a testing dependency is a developer dependency.
I do not believe it's worth the effort for this initial investigation but could be something to explore in the future to provide more control to users.
Dependencies can be added with a valid Git URL, this can be in the SSH format or HTTPS format, and can be from any provider (GitHub, GitLab, Bitbucket, etc).
Dependencies may have one of a few different 'requirements' for versioning:
With current assumptions:
For exactVersion
and revision
, the resolved code cannot be changed and as such will always be ignored by Renovate.
For branch
, the resolved revision (SHA) may be changed but is out of scope for this initial PR.
For the other three, we can use SemVer in order to identify newer versions which fall within the provided ranges.
Semantic Versioning
^1.0.0
or 1.x
?^1.0.0
or 1.x
)Basic constraints are supported (as documented above), but is not as elaborate as some other package managers support.
While the package manager can be used for both, practically speaking, there is no need to tell the difference as they are handled identically.
There is no necessity to pin ranges, but (as per the question below) depending on the range strategy we may approach things slightly differently. This should be a user preference.
Nope, dependencies are linked to a source control platform such as GitHub or GitLab. All the information is in the source URL.
No, the package file format is linked to the Xcode version used to generate it (and subsequently committed into source control). While this could practically change in the future (forcing us to rework how our RegExs work) but given the structure has mostly remained the same for a decade, this isn't a major concern.
No, if we do need to switch extraction in the future, we would do this based on the "objectVersion" which is stored in the file. No user configuration is required.
Xcode generates a "Package.resolved" file which is in a JSON format, this informs Xcode on which version to download given the requirements listed in the project definition file.
Depending on our approach, updating the pins in this file may be required in order to ensure Xcode actually uses the newer version.
Xcode does not, currently, provide a tool to update a single dependency. There is a command xcodebuild -resolvePackageDependencies
which (under some circumstances) updates all dependencies, but this is not reliable.
It is more likely that we will manually alter the JSON file ourselves to pin the correct version.
N/A
N/A
are there any transitive dependencies which need to be resolved for the JSON file?
we really should update both files to have it fully functional.
also we can't use any tool to update the lockfile which isn't at least working on Linux, otherwise it would only supported by self-hosted renovate on apple MacOS.
are there any transitive dependencies which need to be resolved for the JSON file?
If we're going down the route of updating both files, then we will need to handle transitive dependencies too. This is where we open pandora's box of having version conflicts (when two dependencies rely on different versions of the same dependency).
I'm not sure what the best way to handle this is. We have no dependency tree file to use for grouping.
Is it acceptable to just update them each individually, and rely on the user's CI/config to manage incompatibilities? Do we have other managers we can use for examples here?
Example Package.resolved
{
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "63dfa86548c4e5d5c6fd6ed42f638e388cbce529",
"version" : "5.6.0"
}
},
{
"identity" : "cocoalumberjack",
"kind" : "remoteSourceControl",
"location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git",
"state" : {
"revision" : "e518eb6e362df327574ba5e04269cd6d29f40aec",
"version" : "3.7.2"
}
}
]
}
we really should update both files to have it fully functional.
Agreed. It increases complexity, but I think it's better to have a more well-rounded solution which is also more resilient to Xcode shenanigans.
we can't use any tool to update the lockfile which isn't at least working on Linux
Yup, this was fully expected and is one of the many reasons we shouldn't rely on xcodebuild to manage the lock file.
Yeah good work! Keep in mind one important thing in my opinion:
if you have an xcworkspace
the Package.resolved
file is created and updated under the .xcworkspace
folder (.xcworkspace/xcshareddata/swiftpm/Package.resolved
) and the old one under the original .xcodeproj
folder will be ignored.
In my case I'm still having the xcworkspace
for some reasons.
Aye so there's two files we need to look at: • Package.resolved • pbxproj
Both of these really need to be updated to work reliably.
I've just gotten back from Norway so hopefully going to tackle this again - planning to go back to basics a little as trying to reuse the existing solution (which I'm finding has a few flaws) is causing problems.
@Sherlouk do you still plan to move this forward?
Has fallen down my priority list a bit, as I have a homebrew workaround which is working for me - but I do definitely want to support or lead this piece of work.
From when I looked at it, it does appear that there will need to be a bit of a bigger change to the existing Swift solution in order to make it better at handling these different formats.
@Sherlouk any updates regarding this issue? Or could you at least share your homebrew workaround? Would be nice to see renovate being able to be used for iOS projects using SPM.
This issue continues to be the only issue I don't mark as done in GitHub! The only thing stopping me from reaching inbox zero
I still fully intend on getting back to this - just time is a precious commodity and one I don't have excess amounts in right now. Apologies! Hopefully soon 🙏🏼
@Sherlouk I totally understand this! I just wanted to get some feedback regarding this issue. Thank you very much for your answer.
Has this been any update on this/any workarounds that we can use?
Absolutely agree, having Renovate have full support for Swift including Xcode projects would be an amazing feature.
Dependabot now has Swift support but just like Renovate limits this to Package.swift, this is presumably just down to the complexities involved with adding Xcode project support (partially documented in this thread, but also just from experience in trying to add support earlier in the year).
Apple don't make this easy, especially to do it right where you need to update multiple files some with proprietary formatting (which we only know through extensive trial and error).
I'd love to get back to this, but please do not feel like this ticket has to wait for me either if somebody has the time and intrigue to get it done.
While far from comparable, I do know a number of teams who simply run xcodebuild -resolvePackageDependencies -disablePackageRepositoryCache
via their CI on a periodic basis which includes all updates at once.
Comments which are like "I'd really like to see this feature", "Any update?" or "Yes you should really do this feature, would be a shame if someone else did it first and nobody uses Renovate anymore" will be hidden as Spam because they do not help the project in any way while wasting maintainer time with unnecessary notifications.
If you really want to see the feature, you can help identify any remaining requirements gaps or write the code. Thank you to @Sherlouk for doing so, so far! If you're eager for the feature but no intention of contributing, please stick to GitHub's +1 and subscribe features and stop creating noise.
I'm not sure if there's much impact to the pbx using XcodeProj
to parse then save out with modifications.
It would be possible to traverse the dependencies using: https://github.com/tuist/XcodeProj/blob/main/Sources/XcodeProj/Objects/SwiftPackage/XCRemoteSwiftPackageReference.swift#L13
I wonder about hosting the XcodeProj binary on NPM so it can be used as a dependency. https://www.npmjs.com/search?q=XcodeProj
Not directly an answer to a question here, but I was triggered by @markst mentioning XcodeProj
, which we use in the company I work for. The weird workaround I built to get Renovate support for the Xcode projects of the company I work for:
Our renovate bot runs on Linux, so the last command to xcodebuild is probably impossible there because it needs macOS. But it would be cool if all the other steps could be done by Renovate itself. 🙏
I'm not sure if there's much impact to the pbx using
XcodeProj
to parse then save out with modifications.It would be possible to traverse the dependencies using: https://github.com/tuist/XcodeProj/blob/main/Sources/XcodeProj/Objects/SwiftPackage/XCRemoteSwiftPackageReference.swift#L13
I wonder about hosting the XcodeProj binary on NPM so it can be used as a dependency. https://www.npmjs.com/search?q=XcodeProj
if you can compile it to wasm^1, then we can consider using it.
What would you like Renovate to be able to do?
At present renovate doesn't help manage Swift dependencies from the perspective of an Xcode project. Large Xcode projects can have many dependencies and transitive dependencies. Annoyingly, Xcode projects don't handle these dependencies via a
Package.swift
file, like Swift packages themselves do.Did you already have any implementation ideas?
Dependencies are tracked in
project.pbxproj
.Resolved packages can be found in
app.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved