GitPortal

GitPortal - Git Repo Source-Linking Tool

GitPortal is a command-line tool that syncs source code across git repos. It is designed to specifically support sharing code with Kotlin Multiplatform.

GitPortal is Experimental

GitPortal is undergoing heavy development. The unidirectional flow is complete, but arguments and options may change with future versions. Bidirectional flow support is in private testing.

What does GitPortal do?

GitPortal can pull a remote repo’s content into a folder in your local repo. Its design is based on git subrepo, with changes and extensions to support necessary features.

GitPortal uses standard git operations to store, track, and push changes made in your local folder to the remote repo. It is conceptually similar to git submodules, but avoids the complexity of submodules, while supporting additional features needed for our source-sharing goals.

Background

Most teams introducing KMP start by publishing pre-built binaries. AAR files for Android and XCFrameworks for Xcode. While this is simple and low-friction, there are several negatives to this approach. The most critical are the following:

  • iOS developers cannot see or browse Kotlin code
  • iOS developers cannot debug Kotlin code

GitPortal’s current design is focused on implementing a Unidirectional code-flow model with repo-linking.

In “non-jargon” terms, it’s like publishing binaries, but with source code instead of binaries. While that means iOS developers using the KMP code will need a JVM installed, it’s a small bit of “friction” for the benefits that source code visibility provides.

Installation

MacOS

On MacOS, you can install GitPortal through Homebrew.

brew tap touchlab/homebrew-gitportal
brew install gitportal

Other Platforms

For other platforms, GitPortal can be run from a jar file. See GitPortal Releases and download the attached *.jar file for the latest release. All commands and options are identical to those of the native executable builds.

Operation

Before installing and using GitPortal, make sure to read GitPortal for KMP Tutorial to understand why GitPortal works the way it does.

The initial implementation of GitPortal implements a unidirectional code-sharing flow. A KMP git repo is pulled into the Android and iOS app repos, based on tags in the KMP repo.

Diagram of a KMP repo with arrows to app repos

The KMP code is managed in the KMP repo. Versions are marked with git tags. Each app repo can pull in the KMP by tag.

Unidirectional Review

A unidirectional code flow is one where “logic” changes in one repo “flow” to another, but not in the other direction. In KMP-specific terms, it’s how your KMP code from a KMP repo is consumed by the app repos.

A very common setup is to publish binary files, AAR’s for Android, Frameworks for Xcode. That is a unidirectional “code” flow, in the sense that the logic in the KMP repo is consumed by the app repos, and that logic isn’t changed directly in the app repos.

GitPortal implements this same unidirectional flow, but with code instead of binaries. You could accomplish the same thing with git submodules or other options. However, the eventual goal of GitPortal is to implement a bidirectional flow as well, and make switching between the two a config change.

GitPortal is also designed specifically to support this flow. It’s easy to set up and use.

Setup

You’ll need a git repo with KMP code, and at least one version tag. GitPortal pulls that code into your app repos.

From a terminal, cd to the local git clone of your Android or iOS repo. Run the GitPortal setup command.

gitportal setup [library folder] -r [remote KMP repo url] -t [tag from the KMP repo]

That will create a folder in your app repo, with the contents of the KMP matching the tag provided. To create a folder called kmpcode from a KMP repo https://github.com/touchlab/MyKMPCode.git at version tag 1.3.2, the command would be the following:

gitportal setup kmpcode -r https://github.com/touchlab/MyKMPCode.git -t 1.3.2

A folder called kmpcode will be created and the source from the KMP repo will be added there. GitPortal creates a commit after the files are added, and a metadata file at kmpcode/.gitportal. Do not edit this file. It is used for future updates.

Once your linked folder is set up, you can edit your app repo’s files as you normally would. Make changes, push them to the app repo.

A reference to the remote url as well as the tag is kept in the metadata file. Further GitPortal commands can skip those parameters, unless they need to be changed. With the remote, it is unlikely that you’ll need to change the remote address often, or ever, but you can do so by explicitly supplying a different remote on the next operation, or directly editing the GitPortal metadata file, which is a standard git properties file.

Be careful, of course. Editing the other data can break some GitPortal operations. If you ever “break” anything, you can easily recover. Just run GitPortal pull again with all of the parameters specified, and GitPortal will simply overwrite the values.

At the end of the day, GitPortal is a simple tool that uses git operations. There aren’t many “weird states” you can get into, and if you do, hitting “reset” is easy and doesn’t require a full delete and repeated setup.

Dev Process

Developers can edit the app code as they normally would without interacting with GitPortal. You only need to use GitPortal when updating the KMP code with new version tags.

With a unidirectional model, the only restriction is that changes to the KMP files from the app’s repo should not be committed. You can edit them locally, but these changes should be reverted before committing. Updates to the KMP files and repo have a separate process.

This restriction is in place to implement the one-way unidirectional flow of code changes, similar to publishing binary dependencies. GitPortal has a check operation that verifies that the KMP code has no changes, and provides a command you can run to revert inadvertent changes if they are made. The check command is also available as a GitHub Actions workflow that you can run in your repos to enforce the unidirectional model.

In summary, a mobile dev interacts with the native app code and repos the same way they would without KMP. They can locally edit the KMP, but those edits shouldn’t be committed. GitPortal provides easy tools to highlight changes and revert them if necessary.

“Checking” and reverting

To implement a unidirectional flow, CI needs to verify that changes submitted to the app repos don’t include changes in a linked KMP repo folder. GitPortal provides a command to do just that, which can be run from GitHub Actions with template workflows. It is implemented with a Docker image and fairly simple setup instructions, so integrating with other CI systems should be fairly straightforward.

On the client side, if there were KMP changes, you can revert them without complex git operations. GitPortal’s check command is not based on commit but on actual code diffs, so applying a “reverse” diff resolves the issue (although you get an extra commit in your history).

In our current example, the check command would be the following:

gitportal check kmpcode

The remote and tag should be in the metadata file, so GitPortal knows what it is checking against already.

The check command has two possible outcomes:

No changes found in module 'kmpcode'

That means you don’t have KMP changes.

Changes found in module 'kmpcode'
Run the following from the project root to roll back changes
git apply --reverse --directory=kmpcode .git/tmp/aeffff4a-a16b-472f-81fa-9c65807ff873.diff
Failure (CheckDiffFound): Check Failed

That means you had some changes that need to be reverted. GitPortal creates the file .git/tmp/[Random UUID].diff with those changes. You can copy/paste that whole line in a terminal to revert the changes GitPortal found. Then commit and push that.

Of course, if you need those changes, you’ll have to push them up to the KMP repo, review, test, merge, and “publish” them with a tag. If that sounds painful and inefficient, well, it is. That’s the whole problem with a unidirectional model. That’s why you need a scaling plan and a different model for KMP if the goal is to start using KMP more.

KMP Code Version Update

To update the KMP code in the app repo, get the version tag from the KMP repo, and run the following:

gitportal pull kmpcode -t 1.3.3

If the tag you are pulling doesn’t contain the commit from the tag you’re currently on, GitPortal will exit with an error. You can force-pull. This will only apply the changes necessary, and we may change GitPortal to default to “force” in future versions. It’s not a big deal, but be aware of it.

KMP Code Changes

The unidirectional flow prevents commiting KMP changes directly to the app’s repo, but you can push them from the app’s repo to the KMP repo.

Make a local branch

To make the process easier, it is suggested to create a local branch first before pushing changes from the local app repo to the KMP repo. You can stay in the same branch, but you’ll have extra GitPortal “paperwork” commits in your history.

Make KMP code edits, commit, then run:

gitportal push kmpcode -b my_kmp_feature_branch

GitPortal will extract local changes to the KMP code and push them to a branch my_kmp_feature_branch in the KMP repo. If the branch does not exist, it will be created.

From there, you can review and merge the KMP changes in the KMP repo. When you want a new version, add a version tag to the KMP repo.

For our sample, we’ll assume you create the tag 1.3.4 in the KMP repo.

At this point, from GitPortal’s perspective, your linked KMP folder in the local app repo is in an “editing” status. The check operation won’t run because you’re no longer attached to a tagged version. To get that linked folder back into the proper unidirectional mode, you need to use GitPortal to “pull” with a tag.

If you created a new branch for the KMP changes, checkout whatever branch you were on, or main (or whatever your branch process is). Then run:

gitportal pull kmpcode -t 1.3.4

If you’re not in a separate branch, you’ll probably need to add -f to the pull command to force the pull.

After the pull command, your local app repo code will have the tagged changes from the KMP repo, and you can commit this to the app repo.

Operations

GitPortal supports a handful of operations to implement the unidirectional code-flow model. Additional operations and features will likely be needed for a bidirectional model. R&D on the bidirectional model is ongoing currently.

Setup

From your local git repo home folder, run setup to create a local folder and initialize it with the contents of a remote repo.

Pull

Pull updates to a previously setup folder, supplying a specific tag to track the version.

Push

Push changes made in the locally tracked folder to the remote repo, to the supplied branch.

Check

Review the local tracked folder to see if any changes have been made locally. Used to enforce the unidirectional source-sharing model.

Status

Displays the current status of the linked folder.