In this post I’ll revisit a previous post titled Combining Spring Boot with Bower dependencies. After working for a few months with the bifurcated project structure described in that post, we ran into more trouble than we expected.
Tl;DR: Don’t split the repositories if you don’t have to. Use one repository, and have gradle invoke bower install. Also support running npm start/bower install in src/main/resources.
What we thought we’d get
We thought that by splitting the repositories, two different teams with different skill sets could work in isolation on the same target deliverable. Each team could run their relevant sections of code with their preferred stacks, and we’d lean on some Maven tricks with our Continuous Integration environment to stitch it all together nicely.
What we ended up getting
With two different repositories on different stacks, we spent a lot of time diagnosing local development issues. With the back end project, we had Spring Boot’s embedded Tomcat. On the front end, we had Node. Both parts needed things like URL rewriting, redirects, and 404/500 error pages. The configuration for those types of things are container specific, and the configuration we had to develop for the front-end was only applicable to development, and not deployed environments.
Additionally, when we started, all features in the site were available to the anonymous user - no authentication was required. A REST API was then developed that required authentication. The back end had uw-spring-security for this, the front-end repository had no equivalent.
We then wanted a log in user experience, and features that toggled on/off if you were logged in. Without authentication in the front-end repository, developing those features was a guessing game. Open a pull request with what you thought would work, get it merged, and wait until it was deployed to the beta environment to see if it worked. No good at all for productivity to have to wait to ship code to see if it works.
Lastly, in the deployed environment, we had to marry the URL rewriting and redirecting needs of the front-end project in to the embedded Tomcat for Spring Boot. UrlRewriteFilter is actually amazing for this, but it was a piece of code that didn’t exist in dev, only in beta and production. Troubleshooting it was tricky, because you could only troubleshoot on some remote server.
What we did instead
The writing was on the wall: we had to consolidate the front-end and back-end repositories.
The separation was good in theory, but it created too much friction in development. Having to wait to deploy your code to a remote server to see if it gets the job done is no way to work; we need to go back to being able to run the whole application locally for some tasks.
Mechanically, consolidation was a matter of copying the site content from the front-end repository into src/main/resources/static in the back-end repository.
We then needed gradle’s build to invoke bower install. The best Google search result for ‘gradle bower install’ turned out to be craigburke’s bower installer gradle plugin, here’s the relevant section of our build.gradle:
We’re back to having something buildable, but what about the front-end team? They don’t want to run gradle bootRun if they don’t have to.
We have slightly different startup instructions for front-end developers. After cloning the repository:
cd src/main/resources
npm start
In src/main/resources, we have the same package.json, superstatic.json and bower.json that we had in the separate repository. We had to add .bowerrc to src/main/resources with the following contents:
{"directory":"static/bower_components"}
Now when one invokes gradle bowerInstall at the root, or if they run bower install within src/main/resources, the contents of src/main/resources/static/bower_components are equally valid. They don’t match exactly, because the gradle plugin doesn’t fetch the entire repository like true bower does, but the bits we depend on are in the exact same directory hierarchy.
This is working much better. Now a front-end developer has the choice:
Run lean with npm start and not have to worry about the full gradle build. The default proxy configuration points to the beta environment, so they always have a REST API available with live data. Make changes to markup, styles, and not have to restart the container.
If working on something that does require tigher integration with the back end, then the full gradle bootRun is an option to run everything, together, locally.
The one drawback is having to maintain bower dependencies in two places. The project doesn’t experience a lot of volatility in front-end dependencies right now, so we’re living with it.
Addendum: alternative to bower-installer-gradle plugin
In this post I’ll elaborate on a specific scenario in release management: what to do when fresh new features have been merged to master and the minor (or major) version has gone up, but you need a bug fix for the prior releases.
Scenario
We have projectX, which is a mature, Maven-managed project that is using the Maven Release Plugin to distribute artifacts. The project produces a jar that is used as a dependency in a number of downstream projects in production.
Those downstream projects are depending on 3.0.4 releases of projectX. Meanwhile, in projectX, just yesterday some features landed on the master branch that necessitated a version bump to 3.1.0-SNAPSHOT (and subsequent 3.1.0 release).
Today, an issue is identified in projectX that is disrupting one of the downstream projects.
The fix
We’ve captured the issue affecting projectX, a developer was assigned, and produced a fix with a unit test. A pull request is opened targeting the master branch of the project which has been reviewed and merged.
So to get this contribution into a release, if we do nothing else the next available release version from the master branch will be 3.1.1.
The problem
For a downstream project that was depending on 3.0.4 of projectX, having to upgrade to 3.1.1 to get the necessary fix introduces risk.
What if that new feature is unstable? What if that new feature requires refactoring, or configuration changes? We don’t want the potential to fix one bug and introduce another.
Enter maintenance branches.
Path to a 3.0.5 release: Start with a maintenance branch
Start with a clone of the projectX repository, with a remote to the primary repository (referenced as upstream from here on out). Start on the master branch.
Make sure your master branch has the fix: git pull upstream master
Grab all refs that exist in the primary repository: git fetch upstream.
Checkout the very last tag in the 3.0.x series: git checkout -b maintenance projectX-3.0.4
Note that this command creates a new branch named maintenance that originates from the tag projectX-3.0.4. If we just checkout the tag, the subsequent maven command will fail because our working copy is in ‘detached HEAD’.
Use the following maven command to create the maintenance branch: mvn release:branch -DbranchName=rel-3-0-patches -DupdateBranchVersions=true -DupdateWorkingCopyVersions=false[1]
You will be prompted for the next version. For this example, our maintenance branch is going to start at 3.0.5-SNAPSHOT (3.0.4 is the origin release, increment patch by 1 and add -SNAPSHOT). See more examples for this goal, or review this goal’s full reference.
When this command completes, you’ll see a couple things have happened.
You will have 2 local and remote (the repository listed in your pom’s scm block) branches: maintenance and rel-3-0-patches.
A commit will have been added to both of those branches that changes the version element of the pom to the version you were prompted for (3.0.5-SNAPSHOT here) and a change to the tag element the scm block in the pom that references the rel-3-0-patches branch.
An additional commit will exist on the maintenance branch to revert that commit; feel free to either delete the maintenance branch, or re-use it for later maintenance branch creation.
Release from the maintenance branch
Now we have a good branch to apply our original code change and make a maven release:
Find the SHA-1 id of the commit containing the fix. If the fix is spread across multiple commits, you’ll need the ids of each commit, and you’ll have to repeat these commands for each one in the order they were applied. Pro tip: squash bug fix pull requests to one commit to save yourself some time.
git checkout rel-3-0-patches. Double check you are up to date with a git pull upstream rel-3-0-patches.
Cherry-pick the commit: git cherry-pick ab423786...
Create a pull request for the commit targeting the upstream’s rel-3-0-patches branch.
Alternatively, if permissions and team workflow allow, you could just git push upstream rel-3-0-patches. I prefer the pull request, because it allows our Continuous Integration server to confirm the merge is successful and the tests all still pass.
Run the standard maven release: mvn release:prepare && mvn release:perform.
During the prepare phase, you’ll be prompted to release 3.0.5-SNAPSHOT and increment the version of the pom to 3.0.6-SNAPSHOT. It’s important to know that the change to the tag element in the scm block will result in that version bump commit landing on the rel-3-0-patches branch upstream, rather than master (that would be bad).
Without the mvn release:branch
This process can be replicated without the mvn release:branch command, however you MUST make the changes to the pom (changing version to [patch+1]-SNAPSHOT, changing the scm.block) that that command does, or else your maven release will be performed on master and pushed upstream.
That is bad, because now master, which was 3.1.X, now goes back to 3.0.X, and you’ll need to do some major surgery or manual changes to the pom to get back to 3.1.X series. No one likes to see a messy git history like that!
In this post I’ll describe a practical approach to automatically publishing releases of your Gradle managed project using some custom tasks and a Jenkins plugin.
This post was updated on July 15, 2016, edits are inline.
We are working Gradle more and more into our projects. There are still a few Maven plugins and constructs I pine for, but making headway in finding equivalents, sometimes superior.
Here are a couple of user stories describing what we desired:
As a contributor to a software project
I want the continuous integration job testing my contribution to remind me to increment the version
so that I don't forget
and artifact release processes work correctly.
As a custodian for a software project managed with Gradle
I want contributors to the project to be required to increment the project version
so that I can remove the overhead of tracking 'fix versions' down after the fact
and I can see immediately the impact of the change, be it patch, minor feature, or major refactor.
Basically, we want to implement Semantic Versioning, but we don’t want to add a whole ton of human labor to make it happen. We have Jenkins for Continuous Integration, let’s figure out a way to take the repetitive tasks of incrementing the version, tagging, uploading artifacts to Nexus, and a bit of rule enforcement.
Google searches for “gradle equivalent of the maven release plugin” don’t really turn up a clear winner. As I use Gradle more, I think that’s the point - they leave a lot of things to your own creativity.
Idea
Since we don’t have a clear plugin to just start using, our idea started like this:
The Jenkins ‘Merge before build’ step MUST come first. We won’t get a chance to peek at the version property in build.gradle before merging the contribution.
In order to get the ‘last released version’, let’s use a tracked text file (Java Properties file format) in the repository. we can execute git describe.
The gradle command we want the builder to invoke will be gradle clean confirmProjectVersionIncremented test. confirmProjectVersionIncremented doesn’t exist, we’ll write a task for that.
Job 2: poll the master branch of the primary repo, uploadArchives when commits land.
This will fire essentially after a pull request is merged, which means we’ll have a new version in build.gradle.
First, run gradle clean bootRepackage uploadArchives to ship our jar to Nexus.
Then use the Git Publisher post-build step to push the commit, tag and push the tag to the primary repository.
Implementation
The tricks are these fragments, in your build.gradle:
Summary
So far so good.
One drawback we’ve observed so far: you will regularly need to rebase your branches if there is a lot of activity happening in the project. If development is coming in at a trickle, one at a time, nothing iss going to land on master in between you branching and submitting your pull request, no conflicts. But if others are merging at the same time, it’s likely the commit from the job on the master branch that advances the version in build.gradle is going to conflict with your branch.
Update July 15, 2016
A recent edit to the gist shows a simpler way to calculate the previous version using git describe. This is a big improvement, we don’t have to have that dance around the project.last file.
In this post I’ll describe a technique we recently employed to split a site in half, then re-assemble for deployment.
We’ll start with the Spring Boot runnable jar providing a REST API. With a few Maven tricks, we can combine that jar with a static site retrievable via bower, and end up with a new shippable Spring Boot runnable jar with both.
This post was updated on July 14, 2016, see the follow up at the bottom.
Background
We have this site we are working on that has 2 discrete areas of focus:
Integration with one of our institution’s data sources
A single page application user interface for viewing and managing that data
Pretty run of the mill business application. This is a green field project, with each one we get a chance to find a more efficient way to work.
My team is composed of 2 sub-teams (front-end for presentation, back-end for integration), each specializing in the areas of focus described above.
We wanted the back-end team to be able to work in the way they are familiar, and likewise for the front-end team.
We decided to use Spring Boot for the integration project, with Gradle and Java. We wanted to use AngularJS for the front end, but use npm and bower instead.
We also wanted to experiment with a way for the front end team to be able to work without having the overhead of the Spring Boot project. The larger question we wanted to answer:
Is there a way for the front-end team to work in the site using their familiar tool chain and not have to adopt the tool chain that the back-end team uses?
Project Structure
We have two git repositories, one for the back-end using Gradle and Spring Boot, and one for the front-end using Node for a local web server and Bower for dependency management.
Back end team works dutifully in the back-end project. These folks have JDK, Gradle, and an IDE. They write unit and integration tests. The only “front-end” for this application is a Swagger UI if they need to do any end-to-end testing.
Front end team works dutifully in the front end of the site. They don’t need to have the same IDE installed, nor do they need a JDK or Gradle. They work in text editors like Sublime or Atom. Changes they make to the site are picked up immediately, no waiting for a gradle bootRun to re-compile classes and run unit tests.
Angular’s html5mode requires that the server be capable of rewriting/forwarding requests to Angular routes back to /index.html.
We need the REST API provided by the back-end to be served from the same domain, the -proxy fork of superstatic gives us the ability to do that.
The front end developers can proxy the deployed REST API environment of their choice. The back-end team also publishes a docker image for local development if they need something that’s not deployed.
Assembly for deployment
The teams have working releases of their repositories, now we need to stitch them together for deployment.
I’ve shared a git repository demonstrating how this works:
What happens when you run mvn package on this thing?
maven-dependency-plugin fires first, downloading the Spring Boot jar from our Artifact Repository. It then unpacks the jar to the specified <outputDirectory> (best to nest this under ${project.build.directory}).
frontend-maven-plugin fires next. If it hasn’t downloaded node for your system, it will (under ${basedir}/node/). Then it will run npm install (really just to get bower), then it will run bower install. bower.json declares a dependency on our front end project, and now we have a copy sitting in ${project.build.directory} next to the back-end project.
Copy the bower_components (which was generated by the bower install in step 2) folder from the front-end site into META-INF/resources/bower_components in the outputDirectory of step 1.
Copy the rest of the assets of the front end project site (excluding bower_components) into into META-INF/resources in the outputDirectory of step 1.
Copy any beta specific configuration into the project (root of the classpath).
Now sitting on the filesystem is an exploded Spring Boot jar with our assets from the front end site piled into it. The last plugin to fire is the maven-jar-plugin, with a few bits of custom configuration:
classesDirectory is set to be the root of our exploded Spring Boot jar.
A manifestFile - the original from the Spring Boot jar - is explicitly declared. If we don’t do this, the maven-jar-plugin will create one on it’s own that’s missing the properties Spring Boot expects.
The compress flag MUST be false. Spring Boot jars contain jars (inside the lib directory), Spring Boot won’t start properly if these jars get compressed.
Now you have a new Spring Boot jar, composed of both projects and environment specific configuration. Ship this jar to your deployment environment. If you use docker for deployment, use the following Dockerfile:
You may not have to do this, but if your front-end site uses Angular’s $locationProvider.html5Mode(true) you’ll need Spring Boot to handle forwarding requests to your Angular routes to /index.html (or whatever the root of your site is).
There is a great project called UrlRewriteFilter that will give you what you need.
Why didn’t you just put the front-end assets in src/main/resources/META-INF/resources for the back end project?
We’ve done this before, and it’s got a negative impact on the front-end team. The projects they work on use a completely different tool chain.
The front-end team doesn’t have to wait for the back end unit tests to complete, or a full build. They can just run npm start and their front-end app is up and running. No restarts even after a git pull upstream to catch latest merged changes. Using superstatic-with-proxy allows them to encapsulate the back end.
Another question:
Instead of stitching the 2 projects together, couldn’t you just deploy both?
The answer to this is yes. Running the 2 services could give you some resiliency - you’d still be able to deliver content even though the back end of the application is down. Deploying this way would require some magic to get the 2 services running on 2 different ports to be presented by 1 port. You’d need some sort of intermediate layer, like a load balancer or apache with mod_proxy.
superstatic-with-proxy isn’t a good choice for deployment, because in the latest version at time of writing, an HTTP request to a proxied path will cause node to shutdown with an error if the proxied server is offline. That behavior defeats the purpose of trying to mimic the developer’s proxy setup in production.
Summary
So, with this approach, can the front end work in the tool chain of their choice and not have to adopt the tool chain of the back end team? It has been a bit tricky to get to this point, but we believe so. We will continue to experiment, and try to apply this same approach to other projects.
Having one deployable out of the multiple separate sites is nice, simple to deploy. We think it is worth experimenting with deploying the two constituent projects, but there is some complexity there we aren’t quite sure how to resolve.
Here is the output from Gource on the combined activity across 119(!) projects between October 2015 and March 2016:
Areas of Interest
You will notice there are 2 distinct areas of activity; at start, and quickly moved to the right, is the existing Internal Applications team. At about the 5 second mark, you can see a hub of activity drift to the left. That activity is that of our sibling group, the Java section of the WaMS (Web and Mobile Solutions) team. Our teams are joining forces.
Much of the work done by the WaMS-Java team focuses on projects related to Curricular and Academic Operational Data Store (CAOS). Gource only shows the files that are touched in the time period. The longer lines present in projects there indicate deeper nesting of filesystem projects, which is a sign these projects are quite large in size.
In the middle of October, wisclist-custom-interim was added, but has had no activity since that date.
Knowledgebase continues to be isolated (lower right, 15 second mark onward). I have not yet succeeded in getting the priority and resources to help in that large project. Continues to have lots of volatility.
At the end of November (20 second mark), the assets for a legacy project named Itemmaster were imported (shown in purple).
In the middle of December (30 second mark), the assets from the site financial.doit.wisc.edu were imported (also in purple).
In early February, 2016 (1:00 mark in the video) we bootstrapped the new techstore project by importing a customized fork of magescotch, a development environment using Scotchbox for Magento 1.9 and 2.0 development.
In early March (1:10 mark in the video), we upgraded the Magento 2.0 tree in the techstore project from a pre-release version to the (at the time) latest 2.0.2 release. There have been 2 patch releases since we have yet to apply.
My Takeaways
There is a noticeable higher velocity in this video over the last period. Compare the first 15 seconds of this to the original video. This is really encouraging to me for a number of reasons:
Our developers are becoming savvier not just with git but the overall workflow; pull requests, branching, merging, rebasing. Not that they weren’t already, but less learning of the tool and more just using of the tool is apparent.
Units of work are generally smaller in size and more frequent. We are breaking up work better and avoiding the huge contributions of code that are hard to understand.
I intentionally segregated the projects of the WaMS team this time, since our merger is in progress. I can’t wait to see what working together over the next six months will be.
While these visualizations are really good at showing the human collaboration connections, I think they are lacking in showing how these projects actually relate to one another. The connections between projects - how they radiate out from the center - is superficial and a side effect of how gource works and my approach to getting gource to run on the history of multiple projects. I’m craving something that actually shows compile/runtime/deployment dependencies across the portfolio.
I originally intended to produce this annually, but 6 months appears to be a better mid-point to reflect. The source material used to create this video can be found at https://github.com/nblair/internal-apps-visualization.