Writing software in today’s age is becoming easier by the minute. You have courses all over the internet that allow you to accumulate the skills needed for a career in software development. But while it may be easier than ever to learn to develop, but the complexity in software is increasing in development automation and deployment. Most developers have an entire toolbox that they can open to solve most of their problems and that toolbox contains a diverse set of tools that do not necessarily make for a homogenous development experience. Most development environments nowadays consist of a plethora of tooling like Sonar, Jira, CI tooling like Jenkins or Concourse, other static analysis tools, security checks, license checks, … Some of those developers have made custom shell scripts to mitigate that heterogeneity, but in the end that’s just a hack.

Atomist aims to provide a consistent development experience regarding anything that has to do with developer automation and the deployment of your software to one or more deployment targets (think Kubernetes or CloudFoundry). Our goal is to make the developer experience when it comes to automation and deployment as smooth as possible, resulting in happier developers. In this article I’d like to zoom in on one of the development automations that people can use with Atomist to give you a better understanding of what you can achieve.

Inspections

A lot of teams have a certain set of rules. Some of those rules are explicit (enforced by build tool plugins or scripts) but many of those rules are implicit knowledge passed on from developer to developer and if you’re lucky, they’re written down somewhere. The reason for these implicit rules is that they are hard to materialize given the current set of tools or the sheer complexity that would encompass materializing those rules in today’s tools.

Atomist, with its rich API model, makes it possible for developers to codify and materialize these implicit rules into effectively explicit ones and merge their existing explicit rules into a coherent set that’s both easy to maintain and to append on. Communicating the outcome of the enforcement of those rules can then also be streamlined into a single experience for the developers, avoiding the time wasted ploughing through the output produced by all the tooling (like Sonar, PMD or Checkstyle) that is put in place.

Thanks to the rich API and the fact that everything is done in real code, not configuration files, Atomist can utilize the full strength of the NodeJS ecosystem to achieve what other tools struggle with. Using the power of the TypeScript programming language, Atomist allows you to write your automations, which is what code inspections are, in what you’re the most capable in: readable and above all, testable code.

To create a code inspection that reviews your code you need to take a couple of steps and quickly see that it is very easy to achieve this level of automation.

Prerequisite: have an SDM ready

In order to follow these steps, you need to have an SDM on your machine. Have a look at the developer quick start to create a new SDM, but we’ll go through the steps.

Step 1: Make sure you have NodeJS and npm installed on your system

Step 2: Install the Atomist CLI

npm install -g @atomist/cli

Step 3: Create a new SDM by issuing the following command

and choose to create a spring SDM. Fill in the data Atomist prompts you for and it will create a brand new SDM for you. The Spring SDM has the capabilities to build Spring Boot applications, but let’s extend its functionality by adding a code inspection.

Create a code inspection

In Atomist, a code inspection is a function that accepts the project it has been invoked on and potentially a set of parameters that it has received. It returns a result that could have a number of comments on issues the inspection has detected while analyzing the codebase. In case of a review, the return type will be a ProjectReview. So let's start off by creating a skeleton code inspection.

const OldApacheCommonsLangInspector: CodeInspection<ProjectReview> = (project: Project) => {
    const result: ProjectReview = {repoId: project.id, comments: []};    
    return result;
};

Now we can start creating the body of the code review. Let’s write a review that checks whether someone is still using the old Apache Commons libraries, assuming that you want your development team to start using the newer versions.

const OldApacheCommonsLangInspector: CodeInspection<ProjectReview> = async (project: Project) => {
    const result: ProjectReview = {repoId: project.id, comments: []};
    projectUtils.doWithFiles(project, JavaSourceFiles, async f => {
        const content = await f.getContent();
        const regex = /import org\.apache\.commons\.lang\./;
        if(regex.test(content)) {
            result.comments.push({
                severity: "error",
                detail: "Don't use the old Apache Commons Lang",
                category: "old-api",
                subcategory: "commons-lang",
                sourceLocation: {
                    path: f.path,
                    offset: undefined,
                    lineFrom1: findLineNumber(content, regex),
                },
            });
        }
    });
    return result;
};
function findLineNumber(source: string, regex: RegExp): number {
    const lines = source.split("\n");
    const lineFrom0 = lines.findIndex(l => regex.test(l));
    return lineFrom0 + 1;
}

In short, this inspection gets all the Java files in the push, checks whether one has an import from the org.apache.commons.lang package and adds a review comment with severity error in the project review result.

Registering the code inspection in the SDM to run automatically

In the method that creates the SDM instance (in most cases this file is called machine.ts), you need to register the code inspection in that instance. Automatic code inspections are triggered by the AutoCodeInspection goal, so look for that goal in your machine declaration. Next, create a registration for the code inspection:

const codeInspection = new AutoCodeInspection()
    .with({
        name: "old-apache-commons-lang",
        inspection: OldApacheCommonsLangInspector,
    });

Adding a listener for review comments

It’s one thing to detect things using code inspection, it’s another thing to communicate those review comments. In Atomist, a listener can be registered on the AutoCodeInspection goal in order to communicate those comments in whatever form you deem fit. For example, you can use the built-in message client to send review comments back to the channel associated with the project.

You’ll need to register this listener on the goal.

codeInspection.withListener(CodeReviewListener);

This will not only output the code inspection violations in the feed, but will also fail the goal, preventing other goals to proceed if they depend on the AutoCodeInspection goal.

Starting up your SDM in local mode to test the inspection

Atomist has 2 modes: local and team mode (the latter uses the Atomist cloud-connected service). In local mode, everything is isolated to your machine and you don’t need a subscription to the Atomist event hub service platform. It will use local githooks in your projects to capture the events Atomist needs to respond to.

If you have installed the Atomist CLI in the developer quick start, issue the following command to start up your SDM in your console terminal:

atomist start --local

In a separate console terminal start

atomist feed

Testing out the inspection

Open up a console terminal. Create a new project using the build-in generator in the spring sdm.

atomist create spring

You will see in the feed that Atomist is already building your project and has succeeded. Now let’s make it complain about the code inspection we just wrote. Add the following import to one of the .java files.

import org.apache.commons.lang.RandomStringUtils;

Commit the changes. Now Atomist should complain about your changes and indicate where the failure occurred. The feed will show you a detailed message that says which inspection failed and on which line it failed. The AutoCodeInspection goal will have failed as well and if you have the Buildgoal dependent on the AutoCodeInspection goal, you’ll see that it did not run.

Now remove that line again from the code and re-commit. You’ll see that Atomist now proceeds through the AutoCodeInspection goal as there are no violations any more and the other goals are allowed to run.

Running the code inspection manually

You can also run a code inspection as a command in Atomist. Commands are registered on the SDM and get an intent associated with them. An intent is a specific command given to the Atomist CLI that it can interpret. To register a command for the code inspection we just wrote, add the following registration to the SDM:

We can now issue the following command in the command line:

atomist check for old commons lang

If there are violations, Atomist will output them in the reply to this command and in the feed:

error: old-api - Don't use the old Apache Commons Lang library, use Lang3 {"path":"src/main/java/com/example/BlogController.java", "lineFrom1":6}

So what can you do with code inspections?

A lot of times during reviews there are a couple of things that teams know that they need to adhere to, but sometimes forget. So you see quite often comments like “Exported functions need to have a JSDoc”. These are patterns that Atomist can detect easily and warn about those accordingly, saving reviewers precious time and making sure that they can focus on more important aspects of the code. In essence, code inspections are like having another reviewer on your team that is pedantic about all the small things in your code. If it’s a pattern, Atomist can detect it. So think about this: what patterns do you have in your code that you check for in every code review you do? Wouldn’t it be cooler if you didn’t have to check those?