Developers' Best Practices

It is time for D-Net development teams to find a strategy to improve our developement work!

The introduction of Jenkins and Nexus helped a lot in terms of code management, but now it is time to establish a set of guidelines to better co-ordinate our teams.
The following best practices are inspired by CNR developers' common sense, so we do not assume they are written in stones and applies as they are also in your cases.

So, you are strongly invited to comment/update this wiki page, so that we can reach a consensus.

Coding

Most of the D-Net modules have been migrated to from the old build system ant to maven. This implies some changes to the module directories and files. The starting point is the maven settings file describing the maven repository used when resolving module dependencies

<?xml version="1.0" encoding="utf-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
    http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <servers>

        <server>
            <id>dnet45-releases</id>
            <username>{}</username>
            <password>{}</password>
        </server>
        <server>
            <id>dnet45-bootstrap-release</id>
            <username>{}</username>
            <password>{}</password>
        </server>

    </servers>

    <profiles>
        <profile>
            <id>dnet-bootstrap-profile</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <repositories>

                <repository>
                    <id>dnet45-bootstrap-snapshot</id>
                    <name>D-Net 45 Bootstrap Snapshot</name>
                    <url>http://maven.research-infrastructures.eu/nexus/content/repositories/dnet45-bootstrap-snapshot/</url>
                    <releases>
                        <enabled>false</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                    <layout>default</layout>
                </repository>

                <repository>
                    <id>dnet45-bootstrap-release</id>
                    <name>D-Net 45 Bootstrap Release</name>
                    <url>http://maven.research-infrastructures.eu/nexus/content/repositories/dnet45-bootstrap-release/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                    <layout>default</layout>
                </repository>

                <repository>
                    <id>dfm-releases</id>
                    <name>dfm-releases</name>
                    <url>http://maven.research-infrastructures.eu/nexus/content/repositories/dfm-releases/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                    <layout>default</layout>
                </repository>

            </repositories>
        </profile>
    </profiles>
    <pluginGroups>
        <pluginGroup>org.apache.tomcat.maven</pluginGroup>
    </pluginGroups>
</settings>

Maven compliant D-Net module structure:

.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── eu
│   │   │       └── dnetlib
│   │   └── resources
│   │       └── eu
│   │           └── dnetlib
│   └── test
│       ├── java
│       │   └── eu
│       │       └── dnetlib
│       └── resources
│           └── eu
│               └── dnetlib
└── target
    └── classes

Hint: consider to define the svn:ignore property, set on the module root

cd <MODULE_NAME>
svn pe svn:ignore .

.classpath
.project
.settings
target 

Branching

It is always recommended to create a new branch when performing heavy changes to a module, rather than directly into trunk.
Since branching and merging is usually a lot of pain, we can use this section to keep track useful commands:

  • To merge the branch into trunk, you can follow this step by step tutorial: http://www.sepcot.com/blog/2007/04/SVN-Merge-Branch-Trunk
  • If you want to merge changes committed in one or more specific revisions of your branch into trunk, you can use the command:
    cd trunk
    svn merge -c revNumber -c anotherRevNumber https://svn.driver.research-infrastructures.eu/driver/dnet45/modules/yourBranch

Revert to previous version

Because sometimes devs commit things that should not have been committed....
svn merge -r HEAD:<goodRevision> <pathToFileTorevert>
where <goodRevision> is the revision number prior to the wrong commit of <pathToFileTorevert>

Use CI: trust Jenkins!

The developer should clean the .m2/repository/eu local repository from time to time to be sure that there are no modules installed locally.

Modules should be downloaded from Nexus in order to properly resolve dependencies.

Maven dependencies

For information about version conflict resolution with Maven3:

Generally speaking, we suggest to use the keyword LATEST to refer to the last available artifact (snapshot or release) for a module.
Keep in mind that Maven3 considers:
1.0-alpha-1 < 1.0-beta-1 < 1.0-SNAPSHOT < 1.0 < 1.1-SNAPSHOT

How to change the version number of a snapshot?

Depending on the kind of changes you can decide to increase the artifact version according to the following guidelines:
  • BIG CHANGES impact the MAJOR version number. Ex. 0.0.1 --> 1.0.0
    • Examples of big changes are: updates to service interfaces, shared libraries, new functionalities.
  • MINOR CHANGES impact the MINOR version number. Ex. 1.2.5 --> 1.3.0
    • Examples of minor changes are: service internals, non-shared code and libraries, important bug fixes.
  • BUG FIXES impact the BUILD version number. Ex. 1.4.2 --> 1.4.3
    • Examples are small bug fixes that do not affect other components

Release Best Practices

When/Why releasing a module?

  • Generally speaking a developer should release a module when the code is mature enough to be used by others.
  • An early release should be available in case others are relying on a module that is currently under heavy development (i.e., frequent commits that imply frequent snapshot updates), in order to avoid blocking other development activities.
  • Before updating an interface (e.g., service interfaces) or library (e.g., common and utilities modules) a developer MUST ENSURE there is a release of the current version.

How to release?

Note that the maven release plugin works fine with Apache Maven 3.0.5 and that is the version that we suggest you to use.
To know which maven version you are using run:

mvn -version

If you have a newer version (e.g. 3.2.3), then you'll get the error Return code is: 400, ReasonPhrase: Bad Request when running mvn release:perform.
However the jar should have been correctly deployed: we strongly suggest you to check this is true by logging into our Nexus.

These are the steps for the release of a module, given that developer has a fresh checkout of the module to be released.

  1. Add the following at the beginning of your ~/.m2/settings.xml:
    <servers>
     <server>
          <id>dnet45-releases</id>
          <username>your LDAP username</username>
          <password>your LDAP password</password>
       </server>
    </servers>
    
  2. Add the SCM info (change {project.name} with the actual name of the project):
    <scm>
       <developerConnection>scm:svn:https://svn.driver.research-infrastructures.eu/driver/dnet45/modules/{project.name}/trunk</developerConnection>
    </scm>
    
  3. Set the parent to a released parent. Example:
    <parent>
       <groupId>eu.dnetlib</groupId>
       <artifactId>dnet-parent</artifactId>
       <version>1.0.0</version>
    </parent>
    
  4. Ensure there are no snapshots included as dependencies. Snapshot dependencies won't be resolved anymore by maven because released parents have visibility only on the released repository (dnet45-releases). For example, if your module currently depends on
    <dependency>
        <groupId>eu.dnetlib</groupId>
        <artifactId>cnr-misc-utils</artifactId>
        <version>[1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)</version>
    </dependency>
    

    You should change the dependency as follows:
    <dependency>
        <groupId>eu.dnetlib</groupId>
        <artifactId>cnr-misc-utils</artifactId>
        <version>[1.0.0,2.0.0)</version>
    </dependency>
    

    Note that this implies that the dependency module (e.g. cnr-misc-utils) must have been released.
  5. Commit the changes you made in the pom.xml
  6. Run mvn release:prepare and answer the questions. Default answers are usually fine. This will:
    • copy your trunk into another svn folder (tags)
    • update the versions in trunk and tags
  7. Run mvn release:perform. This will deploy the artifact on Nexus in the release repository dnet45-releases.

If you experience any issue, open a ticket to CNR.