(Note: I am not good at editing myself and this turned out longer than I expected when I started. Sorry.)
Motivation
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.
SwiftPM
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: "git@gitlab.private.host.local: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 git@gitlab.private.host.local: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?
Work Around
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.swift
and Package.resolved
and replace it with a working version of the URL for the desired environment.
For example in the CI environment where CI_SERVER_HOST
(e.g., gitlab.private.host.local
) and CI_JOB_TOKEN
(e.g., 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.
Future
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.