Distributing your software component as part of Fiji is an effective way to immediately and easily put it into the hands of many users, as well as to actively participate in the community of ImageJ software development. However, doing so comes with a few corresponding rules.
The following document describes these requirements, as well as associated best practices, for shipping your component as part of the Fiji update site.
- 1 Definition
- 2 Requirements
- 3 Guidelines
- 4 Examples
A "core" Fiji project is one distributed on the Fiji update site. Such projects are subject to the requirements discussed below. Conversely, if you distribute your ImageJ extension on a separate update site, this page does not apply.
Freely accessible source code
A key principle of the Fiji project is:
- If you want to go fast, go alone. If you want to go far, go together.
- - African proverb
There are many corollaries to this wisdom, the most prominent: if you write software in your endeavor to discover new insights, Open Source is the way that brings you farthest. Withholding the source code—like any other method to obstruct other researchers' work, e.g. refusing to share materials and methods—will invariably have the opposite effect in the long run. Likewise, working with interested parties to improve one's project will invariably lead to a much better and stronger result.
As such, components distributed with Fiji must be licensed in a way compatible with the GNU General Public License.
Source hosted on GitHub
Core Fiji development takes place on GitHub. This ensures continuity and visibility, and facilitates collaboration.
There are two possibilities for where to host your project:
- Standard. Repository is hosted in the fiji organization, or a descendant organization (e.g., trakem2). Fiji maintainers help maintain the project.
- External. Repository is hosted in a GitHub organization you control. You alone maintain the project (though Fiji maintainers may submit PRs to help).
The following criteria apply to projects hosted in the fiji organization:
- Each component (i.e., JAR file) lives in its own repository.
- Components use Maven to build:
- As single-module projects
- With the standard Maven directory layout
- Extending the pom-fiji parent POM
- Components use the groupId
- Components are versioned according to SemVer.
- The project uses GitHub Issues for issue tracking.
- The project has a dedicated page here on the ImageJ wiki.
- The Fiji maintainers may make commits and release new versions of the component as needed, so that Fiji as a whole continues to work as intended.
Projects that reside outside the fiji organization are not subject to the requirements above. But it is then the project maintainer's responsibility to ensure the project continues to function properly in up-to-date installations of Fiji. This might entail code changes as ImageJ and Fiji evolve.
Continuous integration: Jenkins
To verify that the Fiji components build without problems, and that all regression tests pass, every Fiji project's source code repository is connected to a job of ImageJ's Jenkins server that builds and tests the source code whenever a new revision is made available. Typically, this job also deploys the Maven artifacts.
To enable a Jenkins link with your repository:
- Configure your repository to add a webhook for the Jenkins (Git plugin)
- Set the
Jenkins URLfield to:
Versioning and dependency convergence
Most Fiji projects use the SemVer versioning scheme: a standard to encourage API consistency without obstructing API improvements.
The minimum requirement for core Fiji projects is to abide by the MAJOR digit portion of SemVer—i.e., if the first digit of the version string increases, it means that the new version is not backwards compatible with the old version. Conversely, if any later digit of the version string increases, it means that the new version is backwards compatible.
This requirement exists to facilitate automated tooling for dependency convergence: the use of compatible dependency versions across all of Fiji. When two (or more) components of Fiji depend on different versions of the same component, it must be possible to verify which version is newer, and whether the newer version is backwards compatible with the old one. As long as the newest required dependencies are indeed backwards compatible, those dependencies are said to converge.
From example, Apache Commons Math v3.x breaks backwards compatibility with Apache Commons Math v2.x. Since both versions share the same package and class names, only one of these versions can be shipped with Fiji. Therefore, all components of Fiji must rely on the same major version: either v2 or v3.
In general, if the rest of the Fiji distribution upgrades to a new major version of a library on which your component depends, your component must also be upgraded to use the new version. Such decisions are typically reached after public discussion on the fiji-devel list.
Best practice: version constants
Many plugins in Fiji contain explicit version constants. Without Maven, in-code constants may make sense as a way to track compatibility. But by Mavenizing for contribution to Fiji, the pom.xml provides a standard mechanism for versioning, allowing migration away from constants in the source code.
Versioning through the pom.xml has several advantages to facilitate reproducible builds, including:
- Standardized scripts to increment versions appropriately.
- No risk of accidentally double-releasing a given version.
- Users and developers see the same version information.
Furthermore, for backwards-compatibility a version can be automatically deduced:
- From the POM. This is the most reliable option. For convenience, scijava-common provides a utility class to assist in version retrieval: VersionUtils.
- Alternatively, the specification or implementation version can be used - for example, as in the LSMReader. Core Fiji libraries follow a convention of setting these versions to match the pom version, and they are set at the manifest level to ensure they are they same for all packages in a given component. However, the two versions are allowed to differ - each package is allowed its own specification and implementation version! Furthermore, classes in a default package will not be able to retrieve either version. So these functions can not be relied on as a general solution.
Fiji and related SciJava software uses Maven, an industry standard to declare metadata about a project, to build projects using said metadata, and to deploy the resulting artifacts to a Maven repository. Such repositories are essentially for developers what update sites are for users.
- The minimum requirement for core Fiji projects is to use a build system (e.g., Maven or Gradle) that automatically deploys required artifacts to the ImageJ Maven repository, such that they can be consumed by downstream code, including other Fiji projects. Required artifacts to deploy include the main JAR and POM files,
- To facilitate this, most Fiji projects inherit a common Maven configuration from the pom-fiji parent project. This configuration ensures that not only the compiled .jar files are deployed, but also the Javadocs and the sources. Therefore, it is strongly encouraged to extend this parent; see the Maven component structure section for details.
- All of Fiji's components are deployed by Jenkins to the ImageJ Maven repository or to OSS Sonatype. That way, all Fiji components can be added easily as dependencies to downstream projects.
- All Fiji components are declared in the toplevel fiji project's POM as dependencies, and declared in the pom-fiji parent as managed dependencies, as part of Fiji's Bill of Materials.
The following guidelines are less technical and more philosophical, but represent best practice for core Fiji components.
Open development process
Developers of Fiji components should invite others to contribute. That entails welcoming developers, acknowledging and working on pull requests, encouraging improvements, working together, enhancing upon each others' work, share insights, etc.
To leverage the power of Open Source, the default for discussions should be the mailing lists, chat rooms and GitHub issues and pull requests. In other words, the question to ask should be "Is there any good reason why this conversation should be private?" instead of the opposite.
Active bug management
Bug reports need to be acknowledged, participation in resolving bugs should be encouraged whenever possible, bugs should not go uncommented for months (we all have times when we are busy e.g. writing a paper; a little message helps the involved people understand), explanations are due when bugs go unresolved for years, etc
Reusability and reliability
Whenever possible, source code should be reused. If necessary, improve the existing source code. Only rewrite from scratch when absolutely necessary.
Make code reusable, i.e. define APIs to use the functionality. This requires a little bit of discipline so that third parties can rely on the interfaces.
Writing regression tests is easy: create a class in the src/test/java/ directory structure and annotate methods with @Test, testing for various assertions (the most common ones are assertEquals(), assertTrue() and assertNotNull()).
In particular when fixing a bug, it is a good idea to write a regression test first, making sure that it actually fails. After that, one should develop the fix, getting a cozy and warm feeling once the regression test passes.
Separation of concerns
New features should be put into the appropriate component. E.g., when adding a general purpose utility, consider contributing to SciJava Common or ImageJ Common instead of bundling it with your specific extension.
The following table provides a few examples of how various Fiji components are structured.
|Classification||Component||Core?1||Update site||License2||Organization||Repository||groupId||Parent POM||Managed in3||BDV4||TrakEM25|
|Simple Neurite Tracer||Fiji||GPLv3||fiji||Simple_Neurite_Tracer||
1 A "core" project is one distributed on the Fiji update site. These projects are subject to the requirements discussed on this page.
2 See the Licensing page for further details.
3 The POM ancestor which manages the version of this component, if any.
4 Whether the component depends on one or more components in the bigdataviewer GitHub organization.
5 Whether the component depends on one or more components in the trakem2 GitHub organization.