(Note: I am not good at editing myself and this turned out longer than I expected when I started. Sorry.)
We are migrating our frameworks to SwiftPM packages. Most of that is going smoothly. The part that is not, and at one time was a blocker, is how to handle dependencies from private authenticated Git repos in our CI environment.
Currently we manage our project dependencies via
git submodules. Submodules have their own challenges and I'm not trying to draw comparisons. They do however have one major redeeming quality in our environment. That is because the CI provider, GitLab, has good support out of the box for handling git submodule cloning.
The tl;dr from the documentation linked above: if you provide a relative repository URL then submodule resolution uses the same protocol and credentials used to clone the parent.
Why is this important? Our developers use Git over SSH to clone/push/pull the repos for their environment. While the CI uses Git over HTTPS to clone using a single use job token. What is more our developers could start using Git over HTTPS or CI Git over SSH and no changes would need to be made to the submodule configuration. Neither use case is prioritized over the other in the configuration.
This brings me to SwiftPM's dependency declaration.
... dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), .package(url: "email@example.com:path/repo.git", .revision("dfbb450d5627faba33c9cb06d203c5b930bd5f8a")), ], ...
And subsequent use of the dependency in an environment that does not use Git over SSH (e.g., our CI environment).
$ swift build Fetching firstname.lastname@example.org:path/repo.git error: failed to clone; Cloning into bare repository '/Users/buildbot/builds/0/path-repo/path/repo/.build/repositories/repo-cdaa868b'... Host key verification failed. fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
Ultimately this whole post boils down to this question. What is the best way to solve that error in SwiftPM?
The work around we have come up with is pretty straightforward. Only use Git over SSH for developer cloning. And use
sed to find the URL in
Package.resolved and replace it with a working version of the URL for the desired environment.
For example in the CI environment where
deadbeef1234) are provided we do something like:
$ sed -i .old 's/git\@'"$CI_SERVER_HOST"':/https:\/\/gitlab-ci-token:'"$CI_JOB_TOKEN"'\@'"$CI_SERVER_HOST"'\//' Package.*
No doubt there is some edge case this will not cover but it works pretty well and has us moving forward again.
Is there a better solution I can source from the community?
Assuming that there isn't already a better way to overcome this. I cannot help but wonder if there is some way for us to provide a similar functionality to the Git submodule feature where we provide a relative URL and SwiftPM resolves the relative URL using
git config --get remote.origin.url (or a more robust version of the same idea).
That way in the
Package.swift we could declare something like
.package(relativeURL: "../../path/repo.git", .revision("dfbb450d5627faba33c9cb06d203c5b930bd5f8a")), and have module resolution just work.
I do understand though there may be no appetite for this in the main project because either: it's not a general problem or it makes too many assumptions about the environment (namely that the Swift package is in a Git repo to begin with). I don't think it hurts to ask.