Best Practices for Integration Testing and Releasing Multiple Packages

I'm looking for best practices to integration test and release multiple related packages.

In CocoaPods, I can update the repo tag and push to a private repo, do whatever testing I need, and then publish the unchanged podspecs to CocoaPods trunk.

Without the concept of a private repo and with tags being immediately released, there does not seem to be a way to create a multi-package release, stage it, and atomically release it.

Any suggestions?

Are you running into issues because the packages depend on each other and they are normally downloaded from a public git repo? In that case, you can actually add the version of the package on your local file system as a dependency in another package. You can then edit and test the packages simultaneously without committing any changes to git.

See this article

When you drag one package into the project navigator for another, make sure the former is not open in another window.

You can also create a workspace and drag both packages into it:

The SpotifyAPI package depends on RegularExpressions. When I first add SpotifyAPI to the workspace, RegularExpressions is downloaded from its git repo, but then after I add RegularExpressions to the workspace, the local version is used instead.

Finally, in your Package.swift file, you can explicitly declare a dependency on the local version of a package:

let package = Package(
    name: "Utilities",
    platforms: [.macOS(.v10_10)],
    products: [
        .library(
            name: "Utilities",
            targets: ["Utilities"]
        )
    ],
    dependencies: [
         // MARK: Depend on the local version of the `RegularExpressions` package
        .package(
            name: "RegularExpressions",
            path: "/Users/pschorn/Swift/Libraries/RegularExpressions"
        )
    ],
    targets: [
        .target(
            name: "Utilities",
            dependencies: ["RegularExpressions"]
        )
    ]
)

1 Like

Thanks for the reply. I didn't know about local packages, but it doesn't seem to add any value beyond editing the Package.swift to use commit hashes instead of versions. It's fine for testing but not releasable.

Either way, I need to do a complex set of Package.swift commits that need to be coordinated with retagging the repos when going from testing to release.

More generally, local testing isn't the issue. I want to be able to run automated tests in GitHub Actions and enable teammates to test the internal candidate release set of packages.

Why not just create a separate development branch for each repository? Furthermore, the Swift Package Manager does support private repositories. See this article.

I have very little experience with Cocoa Pods, so I'm still not even sure what you're trying to do. What is it exactly that Cocoa Pods has that SPM doesn't?

Thanks, I appreciate your trying to understand my question.

Separate branches don't work because tags are associated with repos and not branches. A fork would be a better, but is heavyweight and still has some of the problems described below.

More, specifically for private repositories, I mean a private spec repo for publishing the pod specifications. Private spec repo details are described here. And that is indicative of one of the key differences between CocoaPods and SPM. In CocoaPods, specifications are managed separately from the source that is specified. The podspec may or not be in the same repo as the source; the one that identifies a release is the one that is published to the trunk spec repository; and the spec itself specifies the git tag for identifying the release source.

Consider three packages A is at 1.0 which depends on 2.0 B which depends on 3.0 of C. In CocoaPods, when I want to update to A 1.1 depending on B 2.1 depending on C 3.1, I can update all three repos, tag the repo and push the podspecs to a private repo to enable the three packages to be tested together by any clients with access to the repo. When I'm ready to publish the release, I can push the three podspecs to CocoaPods trunk without needing to touch the A, B, and C repos.

I cannot do the same thing in SPM, because as soon as I tag 3.1 on repo C, it gets installed to all B clients (assuming B specifies "3.0.0" .. < "4.0.0" for its C dependency). Therefore, in order to do integration testing of B and C, I need to change B's Package.swift to something other than its version specification, and similarly for A. Then, when I'm done testing, I need to change it back and release from different git hashes than what I tested.

How about using a workspace to do this? SwiftPM commandline supports these as well, using the --multiroot-data-file option. That way you could check out the packages you want to test together in exactly the versions you want.

Thanks.

Any pointers to docs and/or examples of --multiroot-data-file usage? I'm only finding the source when I search for it.

Not really, it is currently in experimental stage. It is quite heavily being used for testing of some Apple Swift projects, though, so it should be working fine.

In your development branches, you can change the dependency requirements to a branch-based requirement on the development branch of your other packages instead of using tags. This means that the development branches of your packages will depend on the latest commit from the development branches of your other packages. See here. This will require some extra work when you merge the development branch into the main branch for each package (e.g., you'll have to exclude the Package.swift file from the merge), but it should be possible.

This is what I demonstrated in the video in my first post. As Paul mentioned, local testing isn't the issue. He needs to enable teammates to test the internal candidate release set of packages.

I am not sure what the difference is between "local testing" and "test the internal candidate release set of packages" -- aren't these both the same underlying operation of wanting to override a remote dependency with a different one that has been checked out manually?

For integration testing of multiple packages, I'd like to be able to test the actual set of release candidates. If I'm understanding the suggestions correctly, they still require changing the Package.swift files after testing and new tagging before releasing.

If you use a workspace, you could check out the exact versions you are planning to release and test them together, no changes to manifest files are required.