Hello there!
First of all, Thank You for contributing to Apache Fineract! We are grateful for your interest in this project.
Please join the developer mailing list, if you have not already done so, as this is where discussions about this project take place - say Hi there! Please also have a quick look at the Code of Conduct.
The JIRA Dashboard shows what's going on. Create a login - it can take a day or two. If you face difficulties, ask on the mailing list.
You don't need to be a committer to provide pull requests, but Becoming a Committer explains the process of becoming one - just in case...
Here's how to run the set of relatively fast and independent Fineract tests:
./gradlew test -x :twofactor-tests:test -x :oauth2-tests:test -x :integration-tests:test
This runs nearly 1,000 tests and completes in a few minutes on decent hardware. They shouldn't need any special servers/services running.
Running tests with external dependencies is a multi-step process with many moving parts. Sometimes there are arbitrary failures and the prerequisite setup can be daunting. A full local integration test run (on a developer workstation) covering every possible test using every external service and every supported relational database engine could take an entire day - and that's assuming everything is properly configured and runs as expected.
Right now we depend on GitHub to know if “the build” is passing (it's actually multiple builds). The authoritative source of truth for what commands/services/tests to run, how, and when are the files in .github/workflows/. Output from runs based on those configuration files appears at https://github.com/apache/fineract/actions.
Incorrect default Java-related executables may cause test failures. To fix this on Debian and Ubuntu systems, run the following:
export JAVA_HOME=/usr/lib/jvm/zulu21 sudo update-alternatives --set java $JAVA_HOME/bin/java sudo update-alternatives --set javac $JAVA_HOME/bin/javac sudo update-alternatives --set javadoc $JAVA_HOME/bin/javadoc
This would correct, for example, a class file version error. You might see something like this if a Java 11 executable (class file format version 56) was the system default, but the integration tests were using Java 21 (class file format version 65):
UnsupportedClassVersionError: com.example.package/ClassName has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 55.0
The GitHub builds are run in short-lived virtual machines, so locally reproducing the same may require additional effort, such as these extra clean-up procedures:
# Might fix `error: cannot find symbol` or other intermittent failures. # `doc` here is a placeholder for any task(s) you are trying to run. # 💚 This is generally very safe to run between builds. ./gradlew --refresh-dependencies doc # Destroy anything untracked by git. # ⚠️ This may delete something important, e.g. a finely-tuned IDE configuration. git clean --force -dx # Destroy various caches and configs. # ⚠️ This may delete gibibytes of cached data, making the next build very slow. rm -rf ~/.gradle ~/.m2 /tmp/cargo* # Destroy any Java containers left running. # 💚 This is generally very safe to run between builds. ps auxwww | grep [c]argo | awk '{ print $2 }' | xargs -r kill
Integration test runs such as
./gradlew --no-daemon --console=plain test -x :twofactor-tests:test \ -x :oauth2-tests:test :fineract-e2e-tests-runner:test -PdbType=postgresql
in .github/workflows/build-postgresql.yml often take an hour or longer to complete. If you notice the :integration-tests:test task taking significantly less time, say, one minute, gradle may be skipping it. Look for something like this in the test output:
Task :integration-tests:test UP-TO-DATE 👀 Custom actions are attached to task ‘:integration-tests:test’. Build cache key for task ‘:integration-tests:test’ is 6aeeec3f58bf9703d4c100fbaa657f5c Skipping task ‘:integration-tests:test’ as it is up-to-date. Resolve mutations for :integration-tests:cargoStopLocal (Thread[Execution worker Thread 11,5,main]) started. :integration-tests:cargoStopLocal (Thread[Execution worker Thread 11,5,main]) started.
(This is with the --info gradle argument with eyeballs added for emphasis.) The --rerun-tasks gradle argument may help, or you can try destroying ~/.gradle and other clean-up procedures as indicated above, then re-running tests. This is useful for repeated test runs (say, for timing) when gradle would otherwise assume a task is “up-to-date” and not re-run it.
See the next section for testing in Eclipse and here for testing in IntelliJ.
It is possible to run Fineract in Eclipse IDE and also to debug Fineract using Eclipse's debugging facilities. To do this, you need to create the Eclipse project files and import the project into an Eclipse workspace:
./gradlew cleanEclipse eclipseIf you change the project settings (dependencies etc) in Gradle, you should redo step 1 and refresh the project in Eclipse.
You can also use Eclipse JUnit support to run tests in Eclipse (Run As->JUnit Test)
Finally, modifying source code in Eclipse automatically triggers hot code replace to a running instance, allowing you to immediately test your changes
The file gradle/wrapper/gradle-wrapper.jar binary is checked into this projects Git source repository, but won't exist in your copy of the Fineract codebase if you downloaded a released source archive from apache.org. In that case, you need to download it using the commands below:
wget -P gradle/wrapper https://github.com/apache/fineract/raw/develop/gradle/wrapper/gradle-wrapper.jar
or
curl -L https://github.com/apache/fineract/raw/develop/gradle/wrapper/gradle-wrapper.jar > \ gradle/wrapper/gradle-wrapper.jar
./gradlew rat. A report will be generated under build/reports/rat/rat-report.txtRun the following command:
./gradlew doc
Some dependencies are required (e.g. Ghostscript, Graphviz), see .github/workflows/build-documentation.yml for hints.
IDEs such as IntelliJ are useful for editing the AsciiDoc source files while providing a live rendered preview.
HTML rendered from the AsciiDoc source files is also available online at https://fineract.apache.org/docs/current/.
This project enforces its code conventions using checkstyle.xml through Checkstyle and fineractdev-formatter.xml through Spotless. They are configured to run automatically during the normal Gradle build, and fail if there are any violations detected. You can run the following command to automatically fix spotless violations:
./gradlew spotlessApply
Since some checks are present in both Checkstyle and Spotless, the same command can help you fix some of the Checkstyle violations too.
You can also check solely for Spotless violations, but normally don't have to, because regular builds already include this:
./gradlew spotlessCheck
We recommend that you configure your favourite Java IDE to match those conventions. For Eclipse, you can go to Window > Java > Code Style and import the aforementioned config/fineractdev-formatter.xml under formatter section and config/fineractdev-cleanup.xml under cleanup section.
You could also use Checkstyle directly in your IDE, but you don't have to: it may just be more convenient for you. For Eclipse, use https://checkstyle.org/eclipse-cs/ and load our checkstyle.xml into it. For IntelliJ you can use CheckStyle-IDEA.
Changed or added code should ideally have test coverage.
The project uses Jacoco to measure unit tests code coverage. To generate a report run the following command:
./gradlew clean build jacocoTestReport
Generated reports can be found in the build/code-coverage directory.
catch (SomeException e) and then either throw AnotherException("..details..", e) or LOG.error("...context...", e).@Test void testXYZ() throws SomeException, AnotherException..., so that the test fails if the exception happens. Unless you actually really want to test for the occurrence of a problem - in that case, use JUnit's Assert.assertThrows() (but not @Test(expected = SomeException.class)).NullPointerException & Co.System.out and System.err or printStackTrace() anywhere, but always LOG.info() or LOG.error() instead.LOG.error("Could not... details: {}", something, exception)) and never String concatenation (LOG.error("Could not... details: " + something, exception))LOG.error() should be used to inform an “operator” running Fineract who supervises error logs of an unexpected condition. This includes technical problems with an external “environment” (e.g. can't reach a database), and situations which are likely bugs which need to be fixed in the code. They do NOT include e.g. validation errors for incoming API requests - that is signaled through the API response - and does (should) not be logged as an error. (Note that there is no FATAL level in SLF4J; a “FATAL” event should just be logged as an ERROR.)LOG.warn() should be using sparingly. Make up your mind if it's an error (above) - or not!LOG.info() can be used notably for one-time actions taken during start-up. It should typically NOT be used to print out “regular” application usage information. The default logging configuration always outputs the application INFO logs, and in production under load, there‘s really no point to constantly spew out lots of information from frequently traversed paths in the code about what’s going on. (Metrics are a better way.) LOG.info() can be used freely in tests though.LOG.debug() can be used anywhere in the code to log things that may be useful during investigations of specific problems. They are not shown in the default logging configuration, but can be enabled for troubleshooting. Developers should typically “turn down” most LOG.info() which they used while writing a new feature to “follow along what happens during local testing” to LOG.debug() for production before we merge their PRs.LOG.trace() is not used in Fineract.This project uses a number of 3rd-party libraries. We have set up Renovate's bot to automatically raise Pull Requests for our review when new dependencies are available.
Our ClasspathHellDuplicatesCheckRuleTest detects classes that appear in more than 1 JAR. If a version bump in build.gradle causes changes in transitives dependencies, then you may have to add related exclude to our dependencies.gradle. Running ./gradlew dependencies helps to understand what is required.
We request that your commit message includes a FINERACT JIRA issue and a one-liner that describes the changes. Start with an upper case imperative verb (not past form), and a short but concise clear description. (E.g. “FINERACT-821: Add enforced HideUtilityClassConstructor checkstyle”).
If your PR is failing to pass our CI build due to a test failure, then:
AccountingScenarioIntegrationTest you would find FINERACT-899.@Disabled // TODO FINERACT-123 to the respective unstable test (e.g. #774) with the commit message mentioning said JIRA, as always. (Please do NOT just @Disabled any existing tests mixed in as part of your larger PR.)Pull Request Size Limit documents that we cannot accept huge “code dump” Pull Requests, with some related suggestions.
Guideline for new Feature commits involving Refactoring: If you are submitting a PR for a new feature, and it involves refactoring, try to differentiate “new feature code” from “refactored” by placing them in different commits. This helps to review your code faster.
We have an automated bot which marks pull requests as “stale” after a while, and ultimately automatically closes them.
This project‘s committers typically prefer to bring your pull requests in through Rebase and Merge instead of Create a Merge Commit. (If you are unfamiliar with GitHub’s UI regarding this, note the somewhat hidden little triangle drop-down at the bottom of the PR, visible only to committers, not contributors.) This avoids the “merge commits” which we consider to be somewhat “polluting” the project‘s commit log history view. We understand this doesn’t give an easy automatic reference to the original PR (which GitHub automatically adds to the merge commit message it generates), but we consider this an only very minor inconvenience; it's typically relatively easy to find the original PR even just from the commit message, and JIRA.
We expect most proposed PRs to typically consist of a single commit. Committers may use Squash and merge to combine your commits at merge time, and if they do so, will rewrite your commit message as they see fit.
Neither of these two are hard absolute rules, but mere conventions. Multiple commits in single PRs make sense in certain cases (e.g. branch backports).