In many cases, you probably have a CI (Continuous integration) server at the corporate level (Hudson or Jenkins, Bamboo, CruiseControl, Apache Continuum, an in-house CI tool, etc.). As part of the build process, using some CI tasks, you may generate build numbers from revision control system ‘identifiers‘, create tags, push changes, etc.
It’s better when you have the ability to deal with revision control as part of a standard build, without relying on additional scripts or a given environment(i.e. CI tool driving the build).
History and current features
After creating the Ant Command Line Wrapper project on GitHub, I needed to automate few parts of the release process.
Check that the git tree is not modified -> Increment the version number? -> push changes? -> create tags? -> push tags?.
In general, I don’t use Apache Ant, unless it’s a small project or it’s better for everybody (learning curve, productivity, etc.). One thing though, if you don’t need much scripting and you want to stick to your own conventions, Ant is OK for flexible and complex builds!
I first looked at the existing or nonexistent (pun intended) Ant tasks provided by the JGit project. Even though the Wiki page may not be up to date, I count no more than three JGit Ant tasks implemented.
Another promising project, called jgit-ant, added two more Ant tasks for git clone and pull commands. I initially thought about forking the project and contributing back, but jgit-ant has been inactive for a while. I copied some of the code and started re-engineering few bits, while adding more features.
Implemented tasks are Add, Apply, BranchDelete, BranchList, Branch, Checkout, Clean, Clone, Commit, Fetch, Init, Pull, Push, TagDelete, TagList, Tag, UpToDate.
Apache Ant usability
Because Ant is declarative and not all the JGit API is exposed to the Ant tasks, few settings need to be available, in a simple way. Some settings that don’t really make sense may still be exposed, but are defaulted to false for boolean values.
Do you want to perform a simulation(dry-run), when running git commands in a build? Likely not, unless your build is highly interactive, which is probably not the case. When using a build tool, you usually want configurable inputs and zero manual interaction, unless absolutely required.
Design decisions and limitations
What to implement
Trying to implement every git commands and their possible arguments would be difficult and irrational:
- In depth knowledge is needed for git, as well as full understanding of the JGit implementation.
- Not every single Git command will be used in a build process, that’s for sure. Because Ant is declarative first, there are also some limitations, even though you can use scripting via JSR 223.
Missing SSH authentication
JGit supports SSH key authentication using Jsch. I haven’t used Jsch for couple of years and I didn’t have a playground repository setup originally.
In terms of implementation, it shouldn’t be very challenging, but there might be few additional considerations:
- Expose a custom location for SSH keys? Add flexibility for any other relevant settings?
- How will this be tested? Using some mocking framework only?? Embedded Apache Mina SSHD Server?
- User interaction in case of failures? Possible fall-backs to username/password authentication?
Handling one vs. multiple git origins
When doing a git push to a remote repository, you don’t get to manipulate directly the git origin:
- If there’s no remote configuration, it’s populated automatically and the git repository configuration is updated, magically…
- Otherwise, your existing git settings are used, with the assumption that the default JGit settings will work just fine…
An improvement for the git push(eventually any remote-aware command) would be to actually perform the following:
- Expose an origin attribute in the git push command to handle explicit git origins.
- Inspect the git configuration to see if the repository URI is already stored in the configuration, if not add it automatically to the configuration.
Instead of always providing a set of dedicated tasks, many projects tend to prefer tasks nesting to group related actions, among other things…
I decided to have full control over what’s happening, you can only nest git tasks inside a git tasks container. If you need to perform any other action in between, exit the task container, do your work and create another git tasks container.
Having full control over nested tasks gives the ability to inject sensitive defaults and as well as shared properties, common validation, etc.
Error handling and testing strategies
ant-git-tasks commands, hopefully all of them, a custom exception is thrown. You can control whether or not an error can halt the build, by setting a flag called
failonerror. The default value of the
failonerror flag is true, unless set explicitly in the code of a git task implementation (for unknown reasons) :-).
Unit and Integration testing
In the past, I’ve created few Open Source Projects with close to zero unit tests. Some of it was related to the fact that most of those applications were graphical, even though some pieces could still have been unit tested… Doing more testing would have helped preventing few bugs that were not obvious to spot, in addition to creating an overall better design. I also often mocked object manually instead of playing around with Mocking frameworks such as Mockito, JMockit and other some other older libraries.
I hesitated a lot between TestNG and JUnit. I haven’t tried TestNG for ages, at least 5 years or more, if I recall correctly. TestNG also seems to require couple of dependencies that I didn’t want to introduce.
The best way for me to perform testing ended up being a mix of JUnit and AntUnit:
- JUnit is used to unit-test few standard Java classes, not the custom Ant tasks themselves.
- AntUnit is used for integration testing, in a declarative and efficient way. Ant tasks can be tested just the way that they’re intended to be used!
Give ant-git-tasks a drive and see if it can leverage your Ant builds for projects that use git as VCS!