marc-medley
marc-medley

Reputation: 9842

How to use Swift Package Manager with private repos?

How to use the Swift Package Manager with dependencies stored in private repositories?

Adding a username and password can functionally work:

let package = Package(
  name: "MyPackage",
  dependencies: [
    .package(url: "https://username:[email protected]/me/MyPackage.git", .branch("develop"))     
  ]
)

However, the username:password approach has issues including (but not limited to) making the Package.swift username specified, and putting plain text credentials in the repository.

Ideally, a solution would:

  1. not store any plain text or token credentials in the repository.
  2. allow each team member to have their own credentials.

Upvotes: 44

Views: 40923

Answers (5)

marc-medley
marc-medley

Reputation: 9842

A solution, for Unix-like systems, is to use SSH git URLs and add Host configurations to ~/.ssh/config. This approach works for both macOS/Linux command line and Xcode.

Use a ssh form for the dependency url in Package.swift.

// swift-tools-version:4.0
import PackageDescription
    
let package = Package(
  name: "Example",
  dependencies: [
    .package(url: "[email protected]:abc/private-repo.git", .branch("develop") ),
  ],
)

In the above example, gitlab.com.myteam corresponds to a Host in ~/.ssh/config

### GITLAB  WorkTeamOne
Host gitlab.com.workteam
  HostName gitlab.com
  User git
  IdentityFile  ~/.ssh/my_work_key_rsa
  UseKeychain yes     # for macOS keychain
  AddKeysToAgent yes  # for macOS keychain
  PreferredAuthentications publickey
 
### GITLAB  Hobby
Host gitlab.com.hobby
  HostName gitlab.com
  User git
  IdentityFile  ~/.ssh/my_hobby_key_rsa
  UseKeychain yes     # for macOS keychain
  AddKeysToAgent yes  # for macOS keychain
  PreferredAuthentications publickey

Generate and apply SSH key pairs, as needed, for an individual's online git service account.

ssh-keygen \
   -b 4096 \
   -t rsa \
   -C "[email protected]" \
   -f ~/.ssh/my_work_key_rsa

Each team member could setup an individual, corresponding Host ssh configuration. The Host would be the same, however, the actual public/private key pairs (a) are specific to the user, (b) can be managed separately from any of the code development and (c) can be used automatically after the setup.

Upvotes: 43

Matthew Self
Matthew Self

Reputation: 381

I was able to build with a package from a private repo in GitHub actions with HTTPS URLs by creating a .netrc file using the extractions/netrc@v1 action.

  Build:
    runs-on: macos-12
    steps:
      - uses: actions/checkout@v3
      - uses: extractions/netrc@v1
        with:
          machine: github.com
          username: user
          password: ${{ secrets.SWIFT_PACKAGE_MANAGER_PAT }}
      - uses: extractions/netrc@v1
        with:
          machine: api.github.com
          username: user
          password: ${{ secrets.SWIFT_PACKAGE_MANAGER_PAT }}

After this, xcodebuild will use the PAT when accessing the private repo.

I tried to use GITHUB_TOKEN, but it seems that it is restricted to the current repo only. So I created a PAT for a GitHub account that has access to the private repo and added that to the repo secrets.

Upvotes: 6

Ted
Ted

Reputation: 23746

When using spm with private repos that are

  • accessible via vpn or proxies,
  • accessible via ssh
  • with complex ~/ssh/config and /etc/hosts settings,

Note that Xcode keeps its own folder of .ssh and known_hosts.

You can see it in

~/Library/Preferences/com.apple.dt.Xcode.plist

It will look like this

enter image description here

In a ci environment, this is not pleasant and you might get the following errors:

xcodebuild: error: Could not resolve package dependencies: The server SSH fingerprint failed to verify.

xcodebuild: error: Could not resolve package dependencies: Authentication failed because the credentials were rejected

An unknown error occurred. failed to connect to localhost: Connection refused (-1)

You can actually tell Xcode to use standard location of ssh keys and known hosts ~/.ssh via one of the following:

a. This works on container based ci like circle ci

sudo defaults write com.apple.dt.Xcode IDEPackageSupportUseBuiltinSCM YES

b. This works on bare metal

/usr/libexec/Plistbuddy -c "Add :IDEPackageSupportUseBuiltinSCM bool 1" ~/Library/Preferences/com.apple.dt.Xcode.plist

c. If the above didn't work for you try adding -scmProvider system on your xcodebuild command

gym(
    ...
    use_system_scm: true
)
scan(
    ...
    use_system_scm: true
)
  • For running resolve dependencies with SPM having Private Repo
fastlane run spm --use_system_scm true 

  • non-fastlane users
xcodebuild -scmProvider system

Make sure your private repo host is added to ~/.ssh/known_hosts

a. for bitbucket

for ip in $(dig @8.8.8.8 bitbucket.org +short); do ssh-keyscan bitbucket.org,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true 

b. for github

for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true

c. others

ssh-keyscan your-host.com >> ~/.ssh/known_hosts

Upvotes: 38

GingerBreadMane
GingerBreadMane

Reputation: 2697

I am using SPM with a private repo on GitHub. I use the following in my package file:

let package = Package(
  name: "MyPackage",
  dependencies: [
    .package(url: "[email protected]:myusername/mypackage.git", .branch("develop"))     
  ]
)

Then I added a host to my ~/.ssh/config file:

Host github.com
  HostName github.com
  User git
  IdentityFile  ~/.ssh/id_rsa
  PreferredAuthentications publickey

Then I added GitHub to the known hosts file by running the following on the command line:

ssh-keyscan github.com >> ~/.ssh/known_hosts

Finally, I found that when compiling with swift on the command line, it didn't appear possible to use an SSH key with a passphrase so I created a new one without passphrase.

Upvotes: 2

surfrider
surfrider

Reputation: 1425

I've made it without SSH for my iOS project in Xcode. My private repo with package hosted on GitLab, but can be hosted on GitHub, Bitbucket, self-hosted Gitlab.

The process is straight: create a private repo with SPM package on one of the hostings above. Then add it as a dependency in your Xcode project as a usual package. Since the repo is private, Xcode will ask for credentials (in case of Gitlab it's an access token which you can generate in your Gitlab account). Xcode will remember it and will authorize you next time automatically. Thus you (and your colleagues) have to enter credentials only one time at setup. And both of your 1 and 2 conditions are satisfied.

You can manage Source Control Accounts in Xcode -> Preference -> Accounts (press + for add one).

Upvotes: 2

Related Questions