Dao De Code

Share scalafmt configuration with Git submodules

Scalafmt is a great tool for keeping code uniform and avoiding unnecessary diffs during code reviews. We currently use it at work on most of our Scala projects which reside in our enterprise Github. As a team, we picked a set of scalafmt settings that work for everyone in the team and decided to apply it to projects, which live in different Git repos. All was great except one thing which was bothering me for a long time - duplication of scalafmt configuration file.
Each repo was getting its copy of the .scalafmt.conf and they all were the same. And, you know, when you decide that you no longer want your imports to be sorted (probably not), then you will have to go an apply new changes to all the repos. Pretty gross and error-prone.

Originally I wanted this to be on the scalafmt plugins. We use maven at work (yes, Scala with maven in 2018! It still works fine) and scalafmt maven plugin was natural place to ask for help. Which I did and created this issue on github. And recently main scalafmt repo received similar issue, which means I'm not the only one concerned with this.
When I realized that either I will have to implement it myself or do something else, I've tried doing something else.

I figured out that with SBT that should be more or less easy, as you can do arbitrary things in SBT build files. And something like this is even suggested in official scalafmt docs. But in maven land it's not that easy. So I decided to practice some maven-fu and got some results using scm plugin.

<plugin>  
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-scm-plugin</artifactId>
    <version>1.9.5</version>
    <executions>
        <execution>
            <goals>
                <goal>checkout</goal>
            </goals>
            <phase>generate-resources</phase>
            <configuration>
                <checkoutDirectory>${scalafmt.config.checkout.dir}</checkoutDirectory>
                <connectionUrl>scm:git:[email protected]:jozic/scalafmt-config.git</connectionUrl>
                <skipCheckoutIfExists>true</skipCheckoutIfExists>
            </configuration>
        </execution>
    </executions>
</plugin>  

I also had to add maven profile to be able to force updating .scalafmt.conf in case we indeed decide we don't want to sort imports :)

<profile>  
    <id>force-update-scalafmt</id>
    <activation>
        <property>
            <name>updateScalafmtConfig</name>
        </property>
    </activation>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-scm-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>checkout</goal>
                        </goals>
                        <phase>generate-resources</phase>
                        <configuration>
                            <skipCheckoutIfExists>false</skipCheckoutIfExists>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>  

Unfortunately it was quite fragile and broke once I converted a single-module maven project to a multi-module maven project. I was going to try harder and make it work for multi-module projects as well. But then I've got another idea - Git submodules!

I've never used them as never had a good use case for them. But this thing felt like a good candidate to try at least.
So I've re-read git submodules basics, as I've read about them only once long time ago (like in 2010) in Pro Git book and started.

I've tried that on my work projects (that use maven) as well on my pet projects (using SBT) and here is how.

Firstly I've added scalafmt-config repo as a submodule to my other projects

git submodule add https://github.com/jozic/scalafmt-config  

and then I've changed scalafmt config location in build settings. That's it!

To be completely honest, with my first pet project (https://github.com/jozic/scalaj) it took me several commits to get it right. I initially added submodule with ssh url ([email protected]:jozic/scalafmt-config.git), but Travis CI wasn't setup properly. So to keep it simple, I've switched to https. And of course I allowed Travis to access the repo with scalafmt config in Travis settings.
After that Travis was able to checkout my main project and submodule without any other moves from my side.

commits

travis log

Jenkins, CI we use at work, was also smart enough to checkout submodule code without any additional instructions, so there it just worked.

Then I tested the other side of developer experience and cloned the repo. All I have to change from regular git clone url is to add --recursive flag

git clone --recursive [email protected]:jozic/scalaj.git  

. In case you already have a local repo cloned previously, then you need to pull with --recurse-submodules flag

git pull --recurse-submodules  

That all was much easier then all my attempts with maven-fu and I now I realize that this approach is build tool agnostic which is great.

Here're the commits for one of my projects
https://github.com/jozic/scalaj/compare/27157d9b7d0df98e50773ca0e482988309febb4d...07b616f152ff642e5b7a74bf297a92767fa243d4

And here's my scalafmt-config repo that also has some of the usage instructions in README.

It may be that this approach is not that great, as at this moment I've just tried it on several repos and maybe I'm missing something, but so far it looks very promising.

Thanks for reading and I hope this all makes sense and can help someone else.

comments powered by Disqus