FINERACT-1470: Fineract release plugin
This commit is contained in:
committed by
Aleksandar Vidakovic
parent
b185937962
commit
40eb547a5c
@@ -16,6 +16,7 @@ jobs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Build the image
|
||||
run: ./gradlew :fineract-provider:clean :fineract-provider:jibDockerBuild -x test
|
||||
- name: Start the stack
|
||||
|
||||
@@ -16,6 +16,7 @@ jobs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Build the image
|
||||
run: ./gradlew :fineract-provider:clean :fineract-provider:jibDockerBuild -x test
|
||||
- name: Start the stack
|
||||
|
||||
@@ -45,6 +45,7 @@ jobs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@859c33240bd026ce8d5f711f5adcc65c2f8eafc1
|
||||
- name: Verify PostgreSQL connection
|
||||
|
||||
@@ -44,6 +44,7 @@ jobs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@859c33240bd026ce8d5f711f5adcc65c2f8eafc1
|
||||
- name: Verify MariaDB connection
|
||||
|
||||
@@ -34,6 +34,7 @@ jobs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'zulu'
|
||||
cache: gradle
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@859c33240bd026ce8d5f711f5adcc65c2f8eafc1
|
||||
- name: Sonarqube
|
||||
|
||||
+4
-1
@@ -21,4 +21,7 @@ bin/
|
||||
fineract-provider/out/
|
||||
fineract-provider/config/swagger/config.json
|
||||
fineract-provider/config/swagger/fineract-input.yaml
|
||||
licenses
|
||||
licenses
|
||||
**/.asciidoctor/images/*
|
||||
**/.asciidoctor/diagram/*
|
||||
**/images/diag-*.svg
|
||||
@@ -457,59 +457,6 @@ Upgrades sometimes require package name changes. Changed code should ideally ha
|
||||
Our `ClasspathHellDuplicatesCheckRuleTest` detects classes that appear in more than 1 JAR. If a version bump in [`build.gradle`](https://github.com/search?q=repo%3Aapache%2Ffineract+filename%3Abuild.gradle&type=Code&ref=advsearch&l=&l=) causes changes in transitives dependencies, then you may have to add related `exclude` to our [`dependencies.gradle`](https://github.com/apache/fineract/search?q=dependencies.gradle). Running `./gradlew dependencies` helps to understand what is required.
|
||||
|
||||
|
||||
Releasing
|
||||
---------
|
||||
|
||||
[How to Release Apache Fineract](https://cwiki.apache.org/confluence/x/DRwIB) documents the process how we make the source code that is available here in this Git repository into a binary release tar.gz available on http://fineract.apache.org.
|
||||
|
||||
Before you use Gradle to create a release you need to make sure that you provide the proper GPG parameters. You have to options:
|
||||
|
||||
1. Provide the parameters via ~/.gradle/gradle.properties in your home folder:
|
||||
```
|
||||
signing.gnupg.keyName=7890ABCD
|
||||
signing.gnupg.passphrase=secret
|
||||
```
|
||||
|
||||
IMPORTANT: Do not set your GPG secrets in one of the project gradle.properties and double check that you are not accidentally committing them to Git.
|
||||
|
||||
The release command would look then look like this:
|
||||
```
|
||||
./gradlew -Pfineract.release clean build
|
||||
```
|
||||
|
||||
2. Another way to provide these parameters are via project parameters on the command line. A release command would then look like this:
|
||||
```
|
||||
./gradlew -Pfineract.release -Psigning.gnupg.keyName=7890ABCD -Psigning.gnupg.passphrase=secret clean build
|
||||
```
|
||||
|
||||
NOTE: Let's assume your GPG key ID would be "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCD" then you have to use the last 8 characters (i. e. "7890ABCD") for the signing plugin property "signing.gnupg.keyName".
|
||||
|
||||
Above tasks will create the following files in folder build/distributions:
|
||||
|
||||
- binary distribution file: apache-fineract-1.6.0-binary.tar.gz
|
||||
- ASCII armored signature for binary distribution: apache-fineract-1.6.0-binary.tar.gz.asc
|
||||
- SHA512 checksum for binary distribution: apache-fineract-1.6.0-binary.tar.gz.sha512
|
||||
- source distribution file: apache-fineract-1.6.0-src.tar.gz
|
||||
- ASCII armored signature for source distribution: apache-fineract-1.6.0-src.tar.gz.asc
|
||||
- SHA512 checksum for source distribution: apache-fineract-1.6.0-src.tar.gz.sha512
|
||||
|
||||
The signatures are automatically verified by the build script. It will throw an exception if the verification fails.
|
||||
|
||||
Additionally, you can verify the validity of the release distribution files e. g. with:
|
||||
```
|
||||
gpg --verify build/distributions/apache-fineract-1.6.0-binary.tar.gz.asc
|
||||
```
|
||||
|
||||
The output should look somewhat like this:
|
||||
```
|
||||
gpg: assuming signed data in 'build/distributions/apache-fineract-1.6.0-binary.tgz'
|
||||
gpg: Signature made Mi 26 Aug 2020 17:17:45 CEST
|
||||
gpg: using RSA key ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCD
|
||||
gpg: Good signature from "Aleksandar Vidakovic (Apache Fineract Release Manager) <aleks@apache.org>" [ultimate]
|
||||
```
|
||||
|
||||
NOTE: All commands shown above are assuming that the current working directory is the project root folder.
|
||||
|
||||
More Information
|
||||
============
|
||||
More details of the project can be found at <https://cwiki.apache.org/confluence/display/FINERACT>.
|
||||
|
||||
+9
-158
@@ -56,10 +56,9 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||
id "org.barfuin.gradle.taskinfo" version "1.4.0"
|
||||
id 'com.adarshr.test-logger' version '3.2.0'
|
||||
id 'com.diffplug.spotless' version '6.5.0' apply false
|
||||
id 'com.diffplug.spotless' version '6.5.1' apply false
|
||||
id 'org.nosphere.apache.rat' version '0.7.1' apply false
|
||||
id 'com.github.hierynomus.license' version '0.16.1' apply false
|
||||
id 'com.github.jk1.dependency-license-report' version '2.1' apply false
|
||||
@@ -68,7 +67,7 @@ plugins {
|
||||
id 'org.springframework.boot' version '2.6.7' apply false
|
||||
id 'net.ltgt.errorprone' version '2.0.2' apply false
|
||||
id 'io.swagger.core.v3.swagger-gradle-plugin' version '2.2.0' apply false
|
||||
id 'com.gorylenko.gradle-git-properties' version '2.4.0' apply false
|
||||
id 'com.gorylenko.gradle-git-properties' version '2.4.1' apply false
|
||||
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
|
||||
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
|
||||
id 'org.asciidoctor.jvm.epub' version '3.3.2' apply false
|
||||
@@ -83,6 +82,8 @@ plugins {
|
||||
id 'se.thinkcode.cucumber-runner' version '0.0.10' apply false
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.release.gradle"
|
||||
|
||||
description = '''\
|
||||
Run as:
|
||||
gradle clean bootRun'''
|
||||
@@ -91,6 +92,8 @@ ext['groovy.version'] = '3.0.10'
|
||||
ext['swaggerFile'] = "$rootDir/fineract-provider/build/classes/java/main/static/fineract.yaml".toString()
|
||||
|
||||
allprojects {
|
||||
apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle"
|
||||
|
||||
group = 'org.apache.fineract'
|
||||
|
||||
jgitver {
|
||||
@@ -103,7 +106,6 @@ allprojects {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
apply plugin: 'com.adarshr.test-logger'
|
||||
apply plugin: 'com.diffplug.spotless'
|
||||
apply plugin: 'com.github.hierynomus.license'
|
||||
@@ -111,158 +113,6 @@ allprojects {
|
||||
apply plugin: 'project-report'
|
||||
apply plugin: 'com.github.jk1.dependency-license-report'
|
||||
|
||||
// Configuration for the dependency management plugin
|
||||
// https://github.com/spring-gradle-plugins/dependency-management-plugin
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.3.19'
|
||||
mavenBom 'org.springframework.boot:spring-boot-dependencies:2.6.7'
|
||||
mavenBom 'org.jetbrains.kotlin:kotlin-bom:1.6.21'
|
||||
mavenBom 'org.junit:junit-bom:5.8.2'
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.2.1'
|
||||
mavenBom 'io.cucumber:cucumber-bom:7.3.2'
|
||||
mavenBom 'io.netty:netty-bom:4.1.76.Final'
|
||||
mavenBom 'org.mockito:mockito-bom:4.5.1'
|
||||
mavenBom 'io.github.swagger2markup:swagger2markup-bom:1.3.4'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// We use fixed versions, instead of inheriting them from the Spring BOM, to be able to be on more recent ones.
|
||||
// We do not use :+ to get the latest available version available on Maven Central, as that could suddenly break things.
|
||||
// We use the Renovate Bot to automatically propose Pull Requests (PRs) when upgrades for all of these versions are available.
|
||||
|
||||
dependency 'org.slf4j:slf4j-api:1.7.36'
|
||||
dependency 'org.slf4j:slf4j-simple:1.7.36'
|
||||
dependency 'org.slf4j:jcl-over-slf4j:1.7.36'
|
||||
dependency 'org.slf4j:jul-to-slf4j:1.7.36'
|
||||
dependency 'org.slf4j:log4j-over-slf4j:1.7.36'
|
||||
dependency 'ch.qos.logback:logback-core:1.2.11'
|
||||
dependency 'ch.qos.logback:logback-classic:1.2.11'
|
||||
|
||||
dependency 'org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10'
|
||||
dependency 'com.google.guava:guava:31.1-jre'
|
||||
dependency 'com.google.code.gson:gson:2.9.0'
|
||||
dependency 'com.google.truth:truth:1.1.3'
|
||||
dependency 'com.google.truth.extensions:truth-java8-extension:1.1.3'
|
||||
dependency 'com.google.googlejavaformat:google-java-format:1.15.0'
|
||||
dependency ('org.apache.commons:commons-email:1.5') {
|
||||
exclude 'com.sun.mail:javax.mail'
|
||||
exclude 'javax.activation:activation'
|
||||
}
|
||||
dependency 'commons-io:commons-io:2.11.0'
|
||||
dependency 'com.github.librepdf:openpdf:1.3.27'
|
||||
dependency ('org.mnode.ical4j:ical4j:3.2.2') {
|
||||
exclude 'com.sun.mail:javax.mail'
|
||||
}
|
||||
dependency 'org.quartz-scheduler:quartz:2.3.2'
|
||||
dependency 'com.amazonaws:aws-java-sdk-s3:1.12.205'
|
||||
dependency 'org.ehcache:ehcache:3.10.0'
|
||||
dependency 'com.github.spullara.mustache.java:compiler:0.9.10'
|
||||
dependency 'com.jayway.jsonpath:json-path:2.7.0'
|
||||
dependency 'org.apache.tika:tika-core:2.3.0'
|
||||
dependency 'org.apache.httpcomponents:httpclient:4.5.13'
|
||||
dependency 'jakarta.management.j2ee:jakarta.management.j2ee-api:1.1.4'
|
||||
dependency 'jakarta.jms:jakarta.jms-api:2.0.3'
|
||||
dependency 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // Swagger needs exactly this version
|
||||
dependency 'org.apache.bval:org.apache.bval.bundle:2.0.5'
|
||||
dependency 'joda-time:joda-time:2.10.14'
|
||||
|
||||
dependency 'io.github.classgraph:classgraph:4.8.146'
|
||||
dependency 'org.awaitility:awaitility:4.2.0'
|
||||
dependency 'com.github.spotbugs:spotbugs-annotations:4.6.0'
|
||||
dependency 'javax.cache:cache-api:1.1.1'
|
||||
dependency 'org.mock-server:mockserver-junit-jupiter:5.13.2'
|
||||
dependency 'org.webjars:webjars-locator-core:0.50'
|
||||
dependency 'com.icegreen:greenmail-junit5:1.6.8'
|
||||
dependency 'com.intuit.karate:karate-junit5:1.2.0.RC6'
|
||||
|
||||
// fineract client dependencies
|
||||
dependency "com.squareup.retrofit2:retrofit:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:retrofit-mock:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:adapter-java8:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:adapter-rxjava3:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:adapter-guava:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-wire:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-jackson:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-simplexml:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-jaxb:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-java8:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-scalars:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-gson:$retrofitVersion"
|
||||
dependency "com.squareup.retrofit2:converter-protobuf:$retrofitVersion"
|
||||
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:$oltuVersion"
|
||||
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:$oltuVersion"
|
||||
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.httpclient4:$oltuVersion"
|
||||
dependency "com.squareup.okhttp3:okhttp:$okhttpVersion"
|
||||
dependency "com.squareup.okhttp3:okcurl:$okhttpVersion"
|
||||
dependency "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
|
||||
dependency "com.squareup.okhttp3:okhttp-apache:$okhttpVersion"
|
||||
dependency "com.squareup.okhttp3:okhttp-android-support:$okhttpVersion"
|
||||
dependency "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
|
||||
dependency "com.squareup.okhttp3:okhttp-sse:$okhttpVersion"
|
||||
dependency "io.gsonfire:gson-fire:1.8.5"
|
||||
dependency "com.google.code.findbugs:jsr305:3.0.2"
|
||||
dependency "commons-codec:commons-codec:1.15"
|
||||
dependency "org.bouncycastle:bcpkix-jdk15to18:1.70"
|
||||
dependency "org.bouncycastle:bcprov-jdk15to18:1.70"
|
||||
dependency "org.projectlombok:lombok:1.18.24"
|
||||
|
||||
dependency 'org.springdoc:springdoc-openapi-ui:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-webmvc-core:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-common:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-security:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-javadoc:1.6.8'
|
||||
|
||||
dependency ('org.apache.activemq:activemq-broker:5.17.0') {
|
||||
exclude 'javax.annotation:javax.annotation-api'
|
||||
}
|
||||
|
||||
dependency 'io.swagger:swagger-annotations:1.6.6'
|
||||
dependency 'io.swagger.core.v3:swagger-annotations:2.2.0'
|
||||
dependency ('io.swagger.core.v3:swagger-jaxrs2:2.2.0') {
|
||||
exclude 'jakarta.activation:jakarta.activation-api'
|
||||
}
|
||||
dependency ('io.swagger.core.v3:swagger-core:2.2.0') {
|
||||
exclude 'jakarta.activation:jakarta.activation-api'
|
||||
}
|
||||
|
||||
dependency "jakarta.annotation:jakarta.annotation-api:1.3.5"
|
||||
dependency 'jakarta.activation:jakarta.activation-api:1.2.2'
|
||||
dependency ('com.sun.mail:jakarta.mail:1.6.7') {
|
||||
exclude 'com.sun.activation:jakarta.activation'
|
||||
}
|
||||
dependency ('jakarta.xml.bind:jakarta.xml.bind-api:2.3.3') {
|
||||
exclude 'jakarta.activation:jakarta.activation-api'
|
||||
}
|
||||
|
||||
dependency ('org.liquibase:liquibase-core:4.9.1') {
|
||||
exclude 'javax.xml.bind:jaxb-api'
|
||||
}
|
||||
|
||||
dependency ('org.dom4j:dom4j:2.1.3') {
|
||||
exclude 'relaxngDatatype:relaxngDatatype' // already in com.sun.xml.bind:jaxb-osgi:2.3.0.1
|
||||
// FINERACT-940 && FINERACT-966 https://github.com/spotbugs/spotbugs/issues/1128
|
||||
exclude 'xpp3:xpp3'
|
||||
exclude 'pull-parser:pull-parser'
|
||||
}
|
||||
|
||||
dependency 'org.owasp.esapi:esapi:2.3.0.0'
|
||||
|
||||
dependencySet(group: 'org.apache.poi', version: '5.2.2') {
|
||||
entry 'poi'
|
||||
entry 'poi-ooxml'
|
||||
entry 'poi-ooxml-schemas'
|
||||
}
|
||||
|
||||
dependencySet(group: 'io.rest-assured', version: '4.5.1') {
|
||||
entry 'rest-assured'
|
||||
entry 'json-path'
|
||||
entry 'xml-path'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configuration for the sonarqube plugin is now in GitHub Actions
|
||||
|
||||
// Configuration for the spotless plugin
|
||||
@@ -415,6 +265,7 @@ configure(project.fineractJavaProjects) {
|
||||
apply plugin: 'com.github.andygoossens.modernizer'
|
||||
apply plugin: 'se.thinkcode.cucumber-runner'
|
||||
|
||||
|
||||
/* define the valid syntax level for source files */
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
/* define binary compatibility version */
|
||||
@@ -528,8 +379,8 @@ configure(project.fineractJavaProjects) {
|
||||
// Configuration for the Checkstyle plugin
|
||||
// https://docs.gradle.org/current/userguide/checkstyle_plugin.html
|
||||
dependencies {
|
||||
checkstyle 'com.puppycrawl.tools:checkstyle:10.1'
|
||||
checkstyle 'com.github.sevntu-checkstyle:sevntu-checks:1.41.0'
|
||||
checkstyle 'com.puppycrawl.tools:checkstyle:10.2'
|
||||
checkstyle 'com.github.sevntu-checkstyle:sevntu-checks:1.42.0'
|
||||
}
|
||||
|
||||
// Configuration for JaCoCo code coverage task
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||
id 'groovy'
|
||||
id 'java-gradle-plugin'
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
||||
apply from: "${projectDir}/src/main/groovy/org.apache.fineract.dependencies.gradle"
|
||||
|
||||
description = 'Fineract Gradle Plugin'
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.sun.mail:jakarta.mail'
|
||||
implementation 'org.freemarker:freemarker'
|
||||
implementation 'org.tmatesoft.svnkit:svnkit'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on'
|
||||
implementation 'org.bouncycastle:bcpg-jdk15on'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit.ssh.apache'
|
||||
implementation 'com.vdurmont:semver4j'
|
||||
implementation 'org.beryx:text-io'
|
||||
implementation 'commons-io:commons-io'
|
||||
implementation 'com.squareup.okhttp3:okhttp'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor'
|
||||
implementation 'com.squareup.retrofit2:retrofit'
|
||||
implementation 'com.squareup.retrofit2:converter-jackson'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
||||
// Configuration for the dependency management plugin
|
||||
// https://github.com/spring-gradle-plugins/dependency-management-plugin
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom 'org.springframework:spring-framework-bom:5.3.19'
|
||||
mavenBom 'org.springframework.boot:spring-boot-dependencies:2.6.7'
|
||||
mavenBom 'org.jetbrains.kotlin:kotlin-bom:1.6.21'
|
||||
mavenBom 'org.junit:junit-bom:5.8.2'
|
||||
mavenBom 'com.fasterxml.jackson:jackson-bom:2.13.2.1'
|
||||
mavenBom 'io.cucumber:cucumber-bom:7.3.3'
|
||||
mavenBom 'io.netty:netty-bom:4.1.76.Final'
|
||||
mavenBom 'org.mockito:mockito-bom:4.5.1'
|
||||
mavenBom 'io.github.swagger2markup:swagger2markup-bom:1.3.4'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// We use fixed versions, instead of inheriting them from the Spring BOM, to be able to be on more recent ones.
|
||||
// We do not use :+ to get the latest available version available on Maven Central, as that could suddenly break things.
|
||||
// We use the Renovate Bot to automatically propose Pull Requests (PRs) when upgrades for all of these versions are available.
|
||||
|
||||
dependency 'org.slf4j:slf4j-api:1.7.36'
|
||||
dependency 'org.slf4j:slf4j-simple:1.7.36'
|
||||
dependency 'org.slf4j:jcl-over-slf4j:1.7.36'
|
||||
dependency 'org.slf4j:jul-to-slf4j:1.7.36'
|
||||
dependency 'org.slf4j:log4j-over-slf4j:1.7.36'
|
||||
dependency 'ch.qos.logback:logback-core:1.2.11'
|
||||
dependency 'ch.qos.logback:logback-classic:1.2.11'
|
||||
|
||||
dependency 'org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10'
|
||||
dependency 'com.google.guava:guava:31.1-jre'
|
||||
dependency 'com.google.code.gson:gson:2.9.0'
|
||||
dependency 'com.google.truth:truth:1.1.3'
|
||||
dependency 'com.google.truth.extensions:truth-java8-extension:1.1.3'
|
||||
dependency 'com.google.googlejavaformat:google-java-format:1.15.0'
|
||||
dependency ('org.apache.commons:commons-email:1.5') {
|
||||
exclude 'com.sun.mail:javax.mail'
|
||||
exclude 'javax.activation:activation'
|
||||
}
|
||||
dependency 'commons-io:commons-io:2.11.0'
|
||||
dependency 'com.github.librepdf:openpdf:1.3.27'
|
||||
dependency ('org.mnode.ical4j:ical4j:3.2.2') {
|
||||
exclude 'com.sun.mail:javax.mail'
|
||||
}
|
||||
dependency 'org.quartz-scheduler:quartz:2.3.2'
|
||||
dependency 'com.amazonaws:aws-java-sdk-s3:1.12.210'
|
||||
dependency 'org.ehcache:ehcache:3.10.0'
|
||||
dependency 'com.github.spullara.mustache.java:compiler:0.9.10'
|
||||
dependency 'com.jayway.jsonpath:json-path:2.7.0'
|
||||
dependency 'org.apache.tika:tika-core:2.3.0'
|
||||
dependency 'org.apache.httpcomponents:httpclient:4.5.13'
|
||||
dependency 'jakarta.management.j2ee:jakarta.management.j2ee-api:1.1.4'
|
||||
dependency 'jakarta.jms:jakarta.jms-api:2.0.3'
|
||||
dependency 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // Swagger needs exactly this version
|
||||
dependency 'org.apache.bval:org.apache.bval.bundle:2.0.5'
|
||||
dependency 'joda-time:joda-time:2.10.14'
|
||||
|
||||
dependency 'io.github.classgraph:classgraph:4.8.146'
|
||||
dependency 'org.awaitility:awaitility:4.2.0'
|
||||
dependency 'com.github.spotbugs:spotbugs-annotations:4.6.0'
|
||||
dependency 'javax.cache:cache-api:1.1.1'
|
||||
dependency 'org.mock-server:mockserver-junit-jupiter:5.13.2'
|
||||
dependency 'org.webjars:webjars-locator-core:0.50'
|
||||
dependency 'com.icegreen:greenmail-junit5:1.6.8'
|
||||
dependency 'com.intuit.karate:karate-junit5:1.2.0.RC6'
|
||||
|
||||
// fineract client dependencies
|
||||
dependency "com.squareup.retrofit2:retrofit:2.9.0"
|
||||
dependency "com.squareup.retrofit2:retrofit-mock:2.9.0"
|
||||
dependency "com.squareup.retrofit2:adapter-java8:2.9.0"
|
||||
dependency "com.squareup.retrofit2:adapter-rxjava2:2.9.0"
|
||||
dependency "com.squareup.retrofit2:adapter-rxjava3:2.9.0"
|
||||
dependency "com.squareup.retrofit2:adapter-guava:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-wire:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-jackson:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-simplexml:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-jaxb:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-java8:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-scalars:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-gson:2.9.0"
|
||||
dependency "com.squareup.retrofit2:converter-protobuf:2.9.0"
|
||||
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:1.0.1"
|
||||
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.1"
|
||||
dependency "org.apache.oltu.oauth2:org.apache.oltu.oauth2.httpclient4:1.0.1"
|
||||
dependency "com.squareup.okhttp3:okhttp:4.9.3"
|
||||
dependency "com.squareup.okhttp3:okcurl:4.9.3"
|
||||
dependency "com.squareup.okhttp3:logging-interceptor:4.9.3"
|
||||
dependency "com.squareup.okhttp3:okhttp-apache:4.9.3"
|
||||
dependency "com.squareup.okhttp3:okhttp-android-support:4.9.3"
|
||||
dependency "com.squareup.okhttp3:okhttp-urlconnection:4.9.3"
|
||||
dependency "com.squareup.okhttp3:okhttp-sse:4.9.3"
|
||||
dependency "io.gsonfire:gson-fire:1.8.5"
|
||||
dependency "com.google.code.findbugs:jsr305:3.0.2"
|
||||
dependency "commons-codec:commons-codec:1.15"
|
||||
dependency "org.projectlombok:lombok:1.18.24"
|
||||
|
||||
dependency 'org.bouncycastle:bcpkix-jdk15to18:1.70'
|
||||
dependency 'org.bouncycastle:bcprov-jdk15to18:1.70'
|
||||
dependency 'org.bouncycastle:bcprov-jdk15on:1.70'
|
||||
dependency 'org.bouncycastle:bcpg-jdk15on:1.70'
|
||||
|
||||
dependency 'org.eclipse.jgit:org.eclipse.jgit:6.1.0.202203080745-r'
|
||||
dependency 'org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.1.0.202203080745-r'
|
||||
|
||||
dependency 'org.tmatesoft.svnkit:svnkit:1.10.5'
|
||||
dependency 'com.vdurmont:semver4j:3.1.0'
|
||||
dependency 'org.beryx:text-io:3.4.1'
|
||||
|
||||
dependency 'org.springdoc:springdoc-openapi-ui:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-webmvc-core:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-common:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-security:1.6.8'
|
||||
dependency 'org.springdoc:springdoc-openapi-javadoc:1.6.8'
|
||||
|
||||
dependency 'com.google.cloud.sql:mysql-socket-factory-connector-j-8:1.5.0'
|
||||
|
||||
dependency ('org.apache.activemq:activemq-broker:5.17.1') {
|
||||
exclude 'javax.annotation:javax.annotation-api'
|
||||
}
|
||||
|
||||
dependency 'io.swagger:swagger-annotations:1.6.6'
|
||||
dependency 'io.swagger.core.v3:swagger-annotations:2.2.0'
|
||||
dependency ('io.swagger.core.v3:swagger-jaxrs2:2.2.0') {
|
||||
exclude 'jakarta.activation:jakarta.activation-api'
|
||||
}
|
||||
dependency ('io.swagger.core.v3:swagger-core:2.2.0') {
|
||||
exclude 'jakarta.activation:jakarta.activation-api'
|
||||
}
|
||||
|
||||
dependency "jakarta.annotation:jakarta.annotation-api:1.3.5"
|
||||
dependency 'jakarta.activation:jakarta.activation-api:1.2.2'
|
||||
dependency ('com.sun.mail:jakarta.mail:1.6.7') {
|
||||
exclude 'com.sun.activation:jakarta.activation'
|
||||
}
|
||||
dependency ('jakarta.xml.bind:jakarta.xml.bind-api:2.3.3') {
|
||||
exclude 'jakarta.activation:jakarta.activation-api'
|
||||
}
|
||||
|
||||
dependency ('org.liquibase:liquibase-core:4.9.1') {
|
||||
exclude 'javax.xml.bind:jaxb-api'
|
||||
}
|
||||
|
||||
dependency ('org.dom4j:dom4j:2.1.3') {
|
||||
exclude 'relaxngDatatype:relaxngDatatype' // already in com.sun.xml.bind:jaxb-osgi:2.3.0.1
|
||||
// FINERACT-940 && FINERACT-966 https://github.com/spotbugs/spotbugs/issues/1128
|
||||
exclude 'xpp3:xpp3'
|
||||
exclude 'pull-parser:pull-parser'
|
||||
}
|
||||
|
||||
dependency 'org.owasp.esapi:esapi:2.4.0.0'
|
||||
|
||||
dependencySet(group: 'org.apache.poi', version: '5.2.2') {
|
||||
entry 'poi'
|
||||
entry 'poi-ooxml'
|
||||
entry 'poi-ooxml-schemas'
|
||||
}
|
||||
|
||||
dependencySet(group: 'io.rest-assured', version: '4.5.1') {
|
||||
entry 'rest-assured'
|
||||
entry 'json-path'
|
||||
entry 'xml-path'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
apply plugin: 'org.apache.fineract.gradle'
|
||||
apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle"
|
||||
|
||||
fineract {
|
||||
config {
|
||||
username = "${findProperty('fineract.config.username')}"
|
||||
password = "${findProperty('fineract.config.password')}"
|
||||
|
||||
doc {
|
||||
url = 'git@github.com:apache/fineract-site.git'
|
||||
directory = "${System.getProperty("java.io.tmpdir")}/fineract-site"
|
||||
branch = "asf-site"
|
||||
}
|
||||
git {
|
||||
dir = "${projectDir.absolutePath}/.git"
|
||||
sections = [
|
||||
[
|
||||
section: "user",
|
||||
name: "name",
|
||||
value: "${findProperty('fineract.config.name')}",
|
||||
],
|
||||
[
|
||||
section: "user",
|
||||
name: "email",
|
||||
value: "${findProperty('fineract.config.email')}",
|
||||
],
|
||||
[
|
||||
section: "user",
|
||||
name: "signingkey",
|
||||
value: "${findProperty('fineract.config.gnupg.keyName')}",
|
||||
],
|
||||
[
|
||||
section: "commit",
|
||||
name: "gpgsign",
|
||||
value: "true",
|
||||
],
|
||||
]
|
||||
}
|
||||
template {
|
||||
templateDir = "${projectDir}/buildSrc/src/main/resources"
|
||||
}
|
||||
gpg {
|
||||
keyName = "${findProperty('fineract.config.gnupg.keyName')}"
|
||||
publicKeyring = "${findProperty('fineract.config.gnupg.publicKeyring')}"
|
||||
secretKeyring = "${findProperty('fineract.config.gnupg.secretKeyring')}"
|
||||
password = "${findProperty('fineract.config.gnupg.password')}"
|
||||
}
|
||||
smtp {
|
||||
host = 'smtp.gmail.com'
|
||||
username = "${findProperty('fineract.config.smtp.username')}"
|
||||
password = "${findProperty('fineract.config.smtp.password')}"
|
||||
tls = true
|
||||
ssl = true
|
||||
}
|
||||
subversion {
|
||||
username = "${findProperty('fineract.config.username')}"
|
||||
password = "${findProperty('fineract.config.password')}"
|
||||
revision = 'HEAD'
|
||||
}
|
||||
jira {
|
||||
url = 'https://issues.apache.org/jira/rest/api/2/'
|
||||
username = "${findProperty('fineract.config.username')}"
|
||||
password = "${findProperty('fineract.config.password')}"
|
||||
}
|
||||
confluence {
|
||||
url = 'https://cwiki.apache.org/confluence/rest/api/'
|
||||
username = "${findProperty('fineract.config.username')}"
|
||||
password = "${findProperty('fineract.config.password')}"
|
||||
}
|
||||
}
|
||||
|
||||
steps = [
|
||||
test: [
|
||||
order: 0,
|
||||
description: 'Test',
|
||||
email: [
|
||||
from: "${findProperty('fineract.config.email')}",
|
||||
to: "${findProperty('fineract.config.email')}",
|
||||
mime: 'text/plain',
|
||||
subject: "[FINERACT] [TEST] 📣 🏁 🗳️ 🚀 📈 🔀 🌠 Propose new release ${version}",
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl"
|
||||
]
|
||||
],
|
||||
subversion: [
|
||||
url: 'https://dist.apache.org/repos/dist/dev/fineract',
|
||||
directory: "${System.getProperty("java.io.tmpdir")}"
|
||||
],
|
||||
jira: [
|
||||
query: "project in (\'FINERACT\') and fixVersion = ${findProperty('fineract.release.version')} and status in (CLOSED, RESOLVED)"
|
||||
],
|
||||
template: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/jira/changelog.txt.ftl"
|
||||
],
|
||||
gpg: [
|
||||
file: "${projectDir}/buildSrc/build/libs/buildSrc.jar"
|
||||
]
|
||||
],
|
||||
step1: [
|
||||
order: 1,
|
||||
description: 'Send heads-up email to dev mailing list',
|
||||
email: [
|
||||
from: "${findProperty('fineract.config.email')}",
|
||||
name: "${findProperty('fineract.config.name')}",
|
||||
to: "${findProperty('fineract.config.email')}",
|
||||
// to: 'dev@fineract.apache.org',
|
||||
mime: 'text/plain',
|
||||
subjectTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step01.headsup.subject.ftl"
|
||||
],
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl"
|
||||
]
|
||||
]
|
||||
],
|
||||
step2: [
|
||||
order: 2,
|
||||
description: 'Clean up JIRA',
|
||||
],
|
||||
step3: [
|
||||
order: 3,
|
||||
description: 'Create a release branch',
|
||||
email: [
|
||||
from: "${findProperty('fineract.config.email')}",
|
||||
name: "${findProperty('fineract.config.name')}",
|
||||
to: "${findProperty('fineract.config.email')}",
|
||||
// to: 'dev@fineract.apache.org',
|
||||
mime: 'text/plain',
|
||||
subjectTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step03.branch.subject.ftl"
|
||||
],
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step03.branch.message.ftl"
|
||||
]
|
||||
]
|
||||
],
|
||||
step4: [
|
||||
order: 4,
|
||||
description: 'Freeze JIRA version',
|
||||
],
|
||||
step5: [
|
||||
order: 5,
|
||||
description: 'Create release tag',
|
||||
git: [
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/git/release.step05.tag.message.ftl"
|
||||
]
|
||||
]
|
||||
],
|
||||
step6: [
|
||||
order: 6,
|
||||
description: 'Create a distribution',
|
||||
],
|
||||
step7: [
|
||||
order: 7,
|
||||
description: 'Sign the distribution artifacts',
|
||||
gpg: [
|
||||
file: "${rootDir}/fineract-war/build/distributions/apache-fineract-binary-${project.version}.tar.gz"
|
||||
]
|
||||
],
|
||||
step8: [
|
||||
order: 8,
|
||||
description: 'Upload distribution artifacts to staging environment',
|
||||
subversion: [
|
||||
url: 'https://dist.apache.org/repos/dist/dev/fineract',
|
||||
directory: "${System.getProperty("java.io.tmpdir")}/fineract-dist-dev"
|
||||
],
|
||||
],
|
||||
step9: [
|
||||
order: 9,
|
||||
description: 'Verify distribution in staging environment',
|
||||
],
|
||||
step10: [
|
||||
order: 10,
|
||||
description: 'Start voting on the dev mailing list',
|
||||
email: [
|
||||
from: "${findProperty('fineract.config.email')}",
|
||||
name: "${findProperty('fineract.config.name')}",
|
||||
to: "${findProperty('fineract.config.email')}",
|
||||
// to: 'dev@fineract.apache.org',
|
||||
mime: 'text/plain',
|
||||
subjectTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step10.vote.subject.ftl"
|
||||
],
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step10.vote.message.ftl"
|
||||
]
|
||||
]
|
||||
],
|
||||
step11: [
|
||||
order: 11,
|
||||
description: 'Conclude the vote and announce results',
|
||||
email: [
|
||||
from: "${findProperty('fineract.config.email')}",
|
||||
name: "${findProperty('fineract.config.name')}",
|
||||
to: "${findProperty('fineract.config.email')}",
|
||||
// to: 'dev@fineract.apache.org',
|
||||
mime: 'text/plain',
|
||||
subjectTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step11.vote.subject.ftl"
|
||||
],
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step11.vote.message.ftl"
|
||||
]
|
||||
]
|
||||
],
|
||||
step12: [
|
||||
order: 12,
|
||||
description: 'Upload distribution artifacts to release environment',
|
||||
subversion: [
|
||||
url: 'https://dist.apache.org/repos/dist/release/fineract',
|
||||
directory: "${System.getProperty("java.io.tmpdir")}/fineract-dist-release"
|
||||
],
|
||||
],
|
||||
step13: [
|
||||
order: 13,
|
||||
description: 'Close release branch and merge to \'develop\'',
|
||||
],
|
||||
step14: [
|
||||
order: 14,
|
||||
description: 'Update website (not yet automated)',
|
||||
],
|
||||
step15: [
|
||||
order: 15,
|
||||
description: 'Send email to announcement mailing list',
|
||||
jira: [
|
||||
projectId: "12319420",
|
||||
query: "project in (\'FINERACT\') and fixVersion = ${findProperty('fineract.release.version')} and status in (CLOSED, RESOLVED, WONTFIX)"
|
||||
],
|
||||
email: [
|
||||
from: "${findProperty('fineract.config.email')}",
|
||||
name: "${findProperty('fineract.config.name')}",
|
||||
to: "${findProperty('fineract.config.email')}",
|
||||
// to: 'dev@fineract.apache.org',
|
||||
mime: 'text/plain',
|
||||
subjectTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step15.announce.subject.ftl"
|
||||
],
|
||||
messageTemplate: [
|
||||
templateFile: "${projectDir}/buildSrc/src/main/resources/email/release.step15.announce.message.ftl"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.filefilter.NotFileFilter
|
||||
import org.apache.commons.io.filefilter.PrefixFileFilter
|
||||
import org.apache.fineract.gradle.service.*
|
||||
import org.beryx.textio.TextIO
|
||||
import org.beryx.textio.TextIoFactory
|
||||
import org.beryx.textio.TextTerminal
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class FineractPlugin implements Plugin<Project> {
|
||||
private static final Logger log = LoggerFactory.getLogger(FineractPlugin.class)
|
||||
|
||||
private JiraService jiraService
|
||||
private ConfluenceService confluenceService
|
||||
private SubversionService subversionService
|
||||
private EmailService emailService
|
||||
private GpgService gpgService
|
||||
private TemplateService templateService
|
||||
private GitService gitService
|
||||
private Map<String, ?> context;
|
||||
|
||||
static {
|
||||
System.setProperty("org.beryx.textio.TextTerminal", "org.beryx.textio.system.SystemTextTerminal")
|
||||
}
|
||||
|
||||
void apply(Project project) {
|
||||
def extension = project.extensions.create("fineract", FineractPluginExtension, project)
|
||||
|
||||
project.fineract.extensions.create("config", FineractPluginExtension.FineractPluginConfig)
|
||||
project.fineract.config.extensions.create("doc", FineractPluginExtension.FineractPluginConfigDoc)
|
||||
project.fineract.config.extensions.create("template", FineractPluginExtension.FineractPluginConfigTemplate)
|
||||
project.fineract.config.extensions.create("smtp", FineractPluginExtension.FineractPluginConfigSmtp)
|
||||
project.fineract.config.extensions.create("subversion", FineractPluginExtension.FineractPluginConfigSubversion)
|
||||
project.fineract.config.extensions.create("jira", FineractPluginExtension.FineractPluginConfigJira)
|
||||
project.fineract.config.extensions.create("confluence", FineractPluginExtension.FineractPluginConfigConfluence)
|
||||
project.fineract.config.extensions.create("gpg", FineractPluginExtension.FineractPluginConfigGpg)
|
||||
project.fineract.config.extensions.create("git", FineractPluginExtension.FineractPluginConfigGit)
|
||||
|
||||
project.afterEvaluate {
|
||||
this.templateService = new TemplateService(extension.config.template)
|
||||
this.jiraService = new JiraService(extension.config.jira)
|
||||
this.confluenceService = new ConfluenceService(extension.config.confluence)
|
||||
this.subversionService = new SubversionService(extension.config.subversion)
|
||||
this.emailService = new EmailService(extension.config.smtp)
|
||||
this.gpgService = new GpgService(extension.config.gpg)
|
||||
this.gitService = new GitService(extension.config.git, extension.config.gpg)
|
||||
this.context = context(project)
|
||||
}
|
||||
|
||||
project.tasks.register("fineractDocPublish") {
|
||||
dependsOn(":fineract-doc:doc")
|
||||
|
||||
doLast {
|
||||
log.warn("Fineract Publish Doc...")
|
||||
|
||||
def url = extension.config.doc.url?:"git@github.com:apache/fineract-site.git";
|
||||
def branch = extension.config.doc.branch?:"asf-site"
|
||||
def directory = extension.config.doc.directory?:System.getProperty("java.io.tmpdir")+"/fineract-site"
|
||||
|
||||
def d = new File(directory)
|
||||
|
||||
if(d.exists()) {
|
||||
gitService.pull(directory)
|
||||
} else {
|
||||
gitService.clone(url, branch, directory)
|
||||
|
||||
gitService.config(directory, extension.config.git.sections)
|
||||
}
|
||||
|
||||
def source = new File("fineract-doc/build/docs/html/en");
|
||||
def target = new File(directory + "/docs/current")
|
||||
|
||||
if(!target.name) {
|
||||
target.mkdirs()
|
||||
}
|
||||
|
||||
FileUtils.copyDirectory(source, target, new NotFileFilter(new PrefixFileFilter(".asciidoctor")), true)
|
||||
|
||||
gitService.commit(directory)
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register("fineractReleaseTest") {
|
||||
doFirst {
|
||||
// emojis:
|
||||
// https://gist.github.com/parmentf/035de27d6ed1dce0b36a
|
||||
// https://emojipedia.org/
|
||||
// https://unicode.org/emoji/charts/full-emoji-list.html
|
||||
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "test")
|
||||
|
||||
// TODO:
|
||||
// - jira:
|
||||
// - move tickets to new version
|
||||
|
||||
// jira query
|
||||
FineractPluginExtension.FineractPluginJiraParams result = jiraService.search(step.jira)
|
||||
log.warn(">>>>>>>>>>>>>>>> ISSUE: ${result.result[0]?.id} - ${result.result[0]?.key} - ${result.result[0]?.fields?['summary']} - ${result.result[0]?.fields?['fixVersions']}")
|
||||
|
||||
/*
|
||||
|
||||
// confluence create
|
||||
def content = new ConfluenceService.ConfluenceContent(title: "1.7.0 - Apache Fineract",
|
||||
space: new ConfluenceService.ConfluenceSpace(key: "FINERACT"),
|
||||
ancestors: [new ConfluenceService.ConfluenceParent(id: "75974324")],
|
||||
body: new ConfluenceService.ConfluenceBody(storage: new ConfluenceService.ConfluenceStorage(value: "<h1>Release Artifacts</h1>")))
|
||||
confluenceService.createContent(content)
|
||||
|
||||
// confluence get
|
||||
ConfluenceService.ConfluenceContent content = confluenceService.getContent("211881930")
|
||||
log.warn(">>>>>>>>>>>>>>>>>>>>> CONFLUENCE: ${content.id} - ${content.title} - ${content.body?.storage?.value}")
|
||||
|
||||
// confluence update
|
||||
content.body?.storage?.value = "<h1>Release Artifacts</h1><p><ac:structured-macro ac:macro-id=\"ac1cb3cb-84d4-45aa-820b-05cc42d39dd9\" ac:name=\"jira\" ac:schema-version=\"1\"><ac:parameter ac:name=\"server\">ASF JIRA</ac:parameter><ac:parameter ac:name=\"columnIds\">issuekey,summary,issuetype,created,updated,duedate,assignee,reporter,customfield_12311032,customfield_12311037,customfield_12311022,customfield_12311027,priority,status,resolution</ac:parameter><ac:parameter ac:name=\"columns\">key,summary,type,created,updated,due,assignee,reporter,Priority,Priority,Priority,Priority,priority,status,resolution</ac:parameter><ac:parameter ac:name=\"maximumIssues\">50</ac:parameter><ac:parameter ac:name=\"jqlQuery\">project = FINERACT AND fixVersion = 1.7.0 AND status not in (Open)</ac:parameter><ac:parameter ac:name=\"serverId\">5aa69414-a9e9-3523-82ec-879b028fb15b</ac:parameter></ac:structured-macro></p><p><br/></p>"
|
||||
content.space = new ConfluenceService.ConfluenceSpace(key: "FINERACT")
|
||||
content.status = null
|
||||
content.version = new ConfluenceService.ConfluenceVersion(number: 3)
|
||||
|
||||
// confluence update
|
||||
content = confluenceService.updateContent(content.id, content)
|
||||
log.warn(">>>>>>>>>>>>>>>>>>>>> CONFLUENCE: ${content.id} - ${content.title} - ${content.body?.storage?.value}")
|
||||
|
||||
// confluence delete
|
||||
ConfluenceService.ConfluenceResponse response = confluenceService.deleteContent("211881930")
|
||||
log.warn(">>>>>>>>>>>>>>>>>>>>> CONFLUENCE: ${response?.statusCode} - ${response?.message}")
|
||||
|
||||
// jira versions
|
||||
def versions = jiraService.getProjectVersions("12319420")
|
||||
versions.findAll {
|
||||
log.warn(">>>> VERSION: ${it.id} - ${it.name} - ${it.description}")
|
||||
}
|
||||
|
||||
def version = jiraService.getVersion("12351431")
|
||||
log.warn(">>>> !!! VERSION: ${version.id} - ${version.name} - ${version.description} - ${version.archived}")
|
||||
|
||||
version.archived = !version.archived
|
||||
|
||||
jiraService.updateVersion(version)
|
||||
version = jiraService.getVersion("12351431")
|
||||
log.warn(">>>> === VERSION: ${version.id} - ${version.name} - ${version.description} - ${version.archived}")
|
||||
|
||||
// jira projects
|
||||
def projects = jiraService.getProjects()
|
||||
|
||||
projects.findAll {
|
||||
log.warn(">>>>>>>>>>>> PROJECT: ${it.id} - ${it.key} - ${it.name}")
|
||||
}
|
||||
|
||||
// jira info
|
||||
Map<String, Object> info = jiraService.serverInfo()
|
||||
log.warn(">>>>>>>>>>>>>>>> INFO: ${info}")
|
||||
|
||||
// jira query
|
||||
FineractPluginExtension.FineractPluginJiraParams result = jiraService.search(step.jira)
|
||||
log.warn(">>>>>>>>>>>>>>>> INFO: ${result.result.size()}")
|
||||
|
||||
// format with template
|
||||
def tmp = templateService.render(step.template, [issues: result.result])
|
||||
|
||||
log.warn(tmp.output)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: step for creating release Jira ticket missing!
|
||||
|
||||
// step 1
|
||||
project.tasks.register("fineractReleaseStep1") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step1")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
String issue = project.properties?['fineract.release.issue']
|
||||
String date = project.properties?['fineract.release.date']
|
||||
|
||||
if(!version || !issue || !date) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
if(!version) {
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
if(!issue) {
|
||||
issue = textIO.newStringInputReader()
|
||||
.withMaxLength(4)
|
||||
.withPattern("\\d+")
|
||||
.read("Jira Issue");
|
||||
}
|
||||
if(!date) {
|
||||
date = textIO.newStringInputReader()
|
||||
.withMaxLength(10)
|
||||
.withPattern("\\d\\d\\d\\d-\\d\\d-\\d\\d")
|
||||
.read("Date");
|
||||
}
|
||||
|
||||
TextTerminal terminal = textIO.getTextTerminal()
|
||||
terminal.printf("\nResult: %s - FINERACT-%s - %s", version, issue, date)
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
this.context?.project?['fineract.release.issue'] = issue
|
||||
this.context?.project?['fineract.release.date'] = date
|
||||
|
||||
if(step.email) {
|
||||
emailService.send( processEmailParams(step.email, this.context) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 2
|
||||
project.tasks.register("fineractReleaseStep2") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step2")
|
||||
|
||||
// TODO: implement this, see FINERACT-1611
|
||||
|
||||
printInstructions(project, "step2")
|
||||
}
|
||||
}
|
||||
|
||||
// step 3
|
||||
project.tasks.register("fineractReleaseStep3") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step3")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
String date = project.properties?['fineract.release.date']
|
||||
|
||||
if(!version || !date) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
if(!version) {
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
if(!date) {
|
||||
date = textIO.newStringInputReader()
|
||||
.withMaxLength(10)
|
||||
.withPattern("\\d\\d\\d\\d-\\d\\d-\\d\\d")
|
||||
.read("Date");
|
||||
}
|
||||
|
||||
TextTerminal terminal = textIO.getTextTerminal()
|
||||
terminal.printf("\nResult: %s - %s", version, date)
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
// TODO: create release branch, see FINERACT-1611
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
this.context?.project?['fineract.release.date'] = date
|
||||
|
||||
if(step.email) {
|
||||
emailService.send( processEmailParams(step.email, this.context) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 4
|
||||
project.tasks.register("fineractReleaseStep4") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step4")
|
||||
// TODO: implement this, see FINERACT-1611
|
||||
|
||||
printInstructions(project, "step4")
|
||||
}
|
||||
}
|
||||
|
||||
// step 5
|
||||
project.tasks.register("fineractReleaseStep5") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step5")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
|
||||
if(!version) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
if (!version) {
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
}
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
|
||||
def params = processGitParams(step.git, this.context)
|
||||
|
||||
params.tag = params.tag?:version
|
||||
|
||||
gitService.createTag(params.tag, params.message)
|
||||
}
|
||||
}
|
||||
|
||||
// step 6
|
||||
project.tasks.register("fineractReleaseStep6") {
|
||||
dependsOn(":fineract-war:binaryDistTar")
|
||||
}
|
||||
|
||||
// step 7
|
||||
project.tasks.register("fineractReleaseStep7") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step7")
|
||||
|
||||
gpgService.sign(step.gpg)
|
||||
|
||||
def md5 = gpgService.md5(step.gpg)
|
||||
def md5File = new File("${step.gpg.file}.md5")
|
||||
md5File.write md5
|
||||
|
||||
def sha512 = gpgService.sha512(step.gpg)
|
||||
def sha512File = new File("${step.gpg.file}.sha512")
|
||||
sha512File.write sha512
|
||||
}
|
||||
}
|
||||
|
||||
// step 8
|
||||
project.tasks.register("fineractReleaseStep8") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step8")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
|
||||
if(!version) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
subversionService.checkout(step.subversion)
|
||||
|
||||
def directory = step.subversion.directory?:System.getProperty("java.io.tmpdir") + "/fineract-dist-dev"
|
||||
|
||||
def source = new File("fineract-war/build/distributions")
|
||||
def target = new File("${directory}/${version}")
|
||||
|
||||
FileUtils.copyDirectory(source, target, true)
|
||||
|
||||
subversionService.commit(step.subversion)
|
||||
}
|
||||
}
|
||||
|
||||
// step 9
|
||||
project.tasks.register("fineractReleaseStep9") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step9")
|
||||
// TODO: implement this, see FINERACT-1611
|
||||
|
||||
printInstructions(project, "step9")
|
||||
}
|
||||
}
|
||||
|
||||
// step 10
|
||||
project.tasks.register("fineractReleaseStep10") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step10")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
|
||||
if(!version) {
|
||||
TextIO textIO = TextIoFactory.getTextIO();
|
||||
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
|
||||
if(step.email) {
|
||||
emailService.send( processEmailParams(step.email, this.context) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 11
|
||||
project.tasks.register("fineractReleaseStep11") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step11")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
|
||||
if(!version) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
|
||||
def jsonSlurper = new JsonSlurper()
|
||||
def data = jsonSlurper.parse(new File("buildSrc/src/main/resources/vote/result.${version}.json"))
|
||||
|
||||
this.context?.project?['fineract.vote'] = data
|
||||
|
||||
if(step.email) {
|
||||
emailService.send( processEmailParams(step.email, this.context) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 12
|
||||
project.tasks.register("fineractReleaseStep12") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step12")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
|
||||
if(!version) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
subversionService.checkout(step.subversion)
|
||||
|
||||
def directory = step.subversion.directory?:System.getProperty("java.io.tmpdir") + "/fineract-dist-release"
|
||||
|
||||
def source = new File("fineract-war/build/distributions")
|
||||
def target = new File("${directory}/${version}")
|
||||
|
||||
FileUtils.copyDirectory(source, target, true)
|
||||
|
||||
subversionService.commit(step.subversion)
|
||||
}
|
||||
}
|
||||
|
||||
// step 13
|
||||
project.tasks.register("fineractReleaseStep13") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step13")
|
||||
// TODO: implement this, see FINERACT-1611
|
||||
|
||||
printInstructions(project, "step13")
|
||||
}
|
||||
}
|
||||
|
||||
// step 14
|
||||
project.tasks.register("fineractReleaseStep14") {
|
||||
doFirst {
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step14")
|
||||
// TODO: implement this, see FINERACT-1611
|
||||
|
||||
printInstructions(project, "step14")
|
||||
}
|
||||
}
|
||||
|
||||
// step 15
|
||||
project.tasks.register("fineractReleaseStep15") {
|
||||
doFirst {
|
||||
log.warn("Release step 15: send email to announcement mailing list")
|
||||
|
||||
FineractPluginExtension.FineractPluginStep step = step(extension, "step15")
|
||||
|
||||
String version = project.properties?['fineract.release.version']
|
||||
|
||||
if(!version) {
|
||||
TextIO textIO = TextIoFactory.getTextIO()
|
||||
|
||||
version = textIO.newStringInputReader()
|
||||
.withPattern("\\d+.\\d+.\\d+")
|
||||
.read("Release Version");
|
||||
}
|
||||
|
||||
// TODO: input validation, see FINERACT-1610
|
||||
|
||||
FineractPluginExtension.FineractPluginJiraParams issues = jiraService.search(step.jira)
|
||||
|
||||
def versions = jiraService.getProjectVersions(step.jira.projectId)
|
||||
def filteredVersions = versions.findAll {
|
||||
log.warn(">>>> VERSION: ${it.id} - ${it.name} - ${it.description}")
|
||||
it.name == version
|
||||
}
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
this.context?.project?['fineract.release.issues'] = issues.result
|
||||
this.context?.project?['fineract.release.projectId'] = step.jira.projectId
|
||||
this.context?.project?['fineract.release.versionId'] = filteredVersions[0]?.id
|
||||
|
||||
if(step.email) {
|
||||
emailService.send( processEmailParams(step.email, this.context) )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FineractPluginExtension.FineractPluginStep step(FineractPluginExtension extension, String id) {
|
||||
FineractPluginExtension.FineractPluginStep step = extension.steps[id]
|
||||
|
||||
if(step) {
|
||||
log.warn("Fineract release step ${step.order}: ${step.description}.")
|
||||
} else {
|
||||
throw new RuntimeException("Could not find any parameters for step with ID '${id}'")
|
||||
}
|
||||
|
||||
return step
|
||||
}
|
||||
|
||||
private FineractPluginExtension.FineractPluginEmailParams processEmailParams(FineractPluginExtension.FineractPluginEmailParams params, Object data) {
|
||||
if(params.subjectTemplate) {
|
||||
def result = templateService.render(params.subjectTemplate, data)
|
||||
|
||||
params.subject = result.output
|
||||
}
|
||||
if(params.messageTemplate) {
|
||||
def result = templateService.render(params.messageTemplate, data)
|
||||
|
||||
params.message = result.output
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
private FineractPluginExtension.FineractPluginGitParams processGitParams(FineractPluginExtension.FineractPluginGitParams params, Object data) {
|
||||
if(params.messageTemplate) {
|
||||
def result = templateService.render(params.messageTemplate, data)
|
||||
|
||||
params.message = result.output
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
private Map<String, ?> context(Project project) {
|
||||
return Map.of("project", project.getProperties().findAll { it.key != "password"})
|
||||
}
|
||||
|
||||
private void printInstructions(Project project, String step) {
|
||||
String version = project.properties?['fineract.release.version']?:"0.0.0"
|
||||
|
||||
this.context?.project?['fineract.release.version'] = version
|
||||
|
||||
FineractPluginExtension.FineractPluginTemplateParams result = templateService.render(new FineractPluginExtension.FineractPluginTemplateParams(templateFile: new File("buildSrc/src/main/resources/instructions/${step}.txt.ftl")), this.context)
|
||||
|
||||
log.warn(result.output)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle
|
||||
|
||||
import org.apache.fineract.gradle.service.JiraService
|
||||
import org.gradle.api.Project
|
||||
|
||||
class FineractPluginExtension {
|
||||
Project project
|
||||
Map<String, FineractPluginStep> steps = [:]
|
||||
|
||||
FineractPluginExtension(Project project) {
|
||||
this.project = project
|
||||
}
|
||||
|
||||
static class FineractPluginConfig {
|
||||
String username
|
||||
String password
|
||||
}
|
||||
|
||||
static class FineractPluginConfigDoc {
|
||||
String url
|
||||
String directory
|
||||
String branch
|
||||
}
|
||||
|
||||
static class FineractPluginConfigJira {
|
||||
String url
|
||||
String username
|
||||
String password
|
||||
String project
|
||||
}
|
||||
|
||||
static class FineractPluginConfigConfluence {
|
||||
String url
|
||||
String username
|
||||
String password
|
||||
String project
|
||||
}
|
||||
|
||||
static class FineractPluginConfigGpg {
|
||||
String keyName
|
||||
String publicKeyring
|
||||
String secretKeyring
|
||||
String password
|
||||
}
|
||||
|
||||
static class FineractPluginConfigTemplate {
|
||||
String templateDir
|
||||
}
|
||||
|
||||
static class FineractPluginConfigSmtp {
|
||||
String host
|
||||
int port
|
||||
String username
|
||||
String password
|
||||
boolean tls
|
||||
boolean ssl
|
||||
}
|
||||
|
||||
static class FineractPluginConfigGit {
|
||||
String username
|
||||
String password
|
||||
String dir
|
||||
boolean dryRun
|
||||
List<FineractPluginConfigGitSection> sections
|
||||
}
|
||||
|
||||
static class FineractPluginConfigGitSection {
|
||||
String section
|
||||
String subsection
|
||||
String name
|
||||
String value
|
||||
}
|
||||
|
||||
static class FineractPluginConfigSubversion {
|
||||
String username
|
||||
String password
|
||||
String revision
|
||||
}
|
||||
|
||||
static class FineractPluginStep {
|
||||
int order
|
||||
String description
|
||||
FineractPluginEmailParams email
|
||||
FineractPluginJiraParams jira
|
||||
FineractPluginTemplateParams template
|
||||
FineractPluginConfluenceParams confluence
|
||||
FineractPluginGitParams git
|
||||
FineractPluginSubversionParams subversion
|
||||
FineractPluginGpgParams gpg
|
||||
}
|
||||
|
||||
static class FineractPluginEmailParams {
|
||||
String from
|
||||
String name
|
||||
String to
|
||||
String cc
|
||||
String bcc
|
||||
String mime
|
||||
String subject
|
||||
FineractPluginTemplateParams subjectTemplate
|
||||
String message
|
||||
FineractPluginTemplateParams messageTemplate
|
||||
}
|
||||
|
||||
static class FineractPluginJiraParams {
|
||||
String command
|
||||
String projectId
|
||||
String fields = "*all"
|
||||
String query
|
||||
List<JiraService.JiraIssue> result = new ArrayList<>()
|
||||
int pageOffset = 0
|
||||
int pageSize= 50
|
||||
int total = 1000
|
||||
List<String> includes = ["summary", "status", "assignee", "fixVersions"]
|
||||
}
|
||||
|
||||
static class FineractPluginConfluenceParams {
|
||||
String title
|
||||
String content
|
||||
Integer ancestor
|
||||
}
|
||||
|
||||
static class FineractPluginGitParams {
|
||||
String tag
|
||||
String message
|
||||
FineractPluginTemplateParams messageTemplate
|
||||
}
|
||||
|
||||
static class FineractPluginSubversionParams {
|
||||
String url
|
||||
String command
|
||||
String revision = "HEAD"
|
||||
String directory
|
||||
}
|
||||
|
||||
static class FineractPluginTemplateParams {
|
||||
String template
|
||||
String templateFile
|
||||
String output
|
||||
String outputFile
|
||||
}
|
||||
|
||||
static class FineractPluginGpgParams {
|
||||
String file
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import okhttp3.ConnectionPool
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.apache.fineract.gradle.FineractPluginExtension.FineractPluginConfigConfluence
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
||||
import retrofit2.http.*
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ConfluenceService {
|
||||
private static final Logger log = LoggerFactory.getLogger(ConfluenceService.class)
|
||||
|
||||
private ConfluenceApi api
|
||||
|
||||
ConfluenceService(FineractPluginConfigConfluence config) {
|
||||
def credentials = Credentials.basic(config.username, config.password)
|
||||
|
||||
final ConnectionPool connectionPool = new ConnectionPool(5, 60, TimeUnit.SECONDS);
|
||||
|
||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(log::debug)
|
||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||
|
||||
OkHttpClient.Builder okClient = new OkHttpClient.Builder()
|
||||
.connectionPool(connectionPool)
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
|
||||
def request = chain.request()
|
||||
request = request.newBuilder().header("Authorization", credentials).build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
})
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
|
||||
Retrofit.Builder builder = new Retrofit.Builder()
|
||||
.baseUrl(config.url)
|
||||
.client(okClient.build())
|
||||
.addConverterFactory(JacksonConverterFactory.create(mapper))
|
||||
|
||||
Retrofit retrofit = builder.build()
|
||||
|
||||
this.api = retrofit.create(ConfluenceApi.class)
|
||||
}
|
||||
|
||||
ConfluenceContent getContent(String id) {
|
||||
this.api.getContentById(id).execute().body()
|
||||
}
|
||||
|
||||
ConfluenceContent createContent(ConfluenceContent content) {
|
||||
this.api.postContent(content).execute().body()
|
||||
}
|
||||
|
||||
ConfluenceContent updateContent(String id, ConfluenceContent content) {
|
||||
this.api.putContent(id, content).execute().body()
|
||||
}
|
||||
|
||||
ConfluenceResponse deleteContent(String id) {
|
||||
this.api.deleteContentById(id).execute().body()
|
||||
}
|
||||
|
||||
static class ConfluenceContent {
|
||||
String id
|
||||
String type
|
||||
String title
|
||||
String status
|
||||
String self
|
||||
List<ConfluenceParent> ancestors
|
||||
ConfluenceSpace space
|
||||
ConfluenceBody body
|
||||
ConfluenceVersion version
|
||||
ConfluenceHistory history
|
||||
}
|
||||
|
||||
static class ConfluenceContentResult {
|
||||
List<ConfluenceContent> results
|
||||
private int start
|
||||
private int limit
|
||||
private int size
|
||||
}
|
||||
|
||||
static class ConfluenceParent {
|
||||
String id
|
||||
String type
|
||||
}
|
||||
|
||||
static class ConfluenceVersion {
|
||||
ConfluenceCreatedBy by
|
||||
Date when
|
||||
String message
|
||||
Integer number
|
||||
}
|
||||
|
||||
static class ConfluenceCreatedBy {
|
||||
String username
|
||||
String displayName
|
||||
}
|
||||
|
||||
static class ConfluenceHistory {
|
||||
boolean latest
|
||||
ConfluenceCreatedBy createdBy
|
||||
ConfluenceVersion nextVersion
|
||||
ConfluenceVersion previousVersion
|
||||
ConfluenceVersion lastUpdated
|
||||
}
|
||||
|
||||
static class ConfluenceSpace {
|
||||
String key
|
||||
String name
|
||||
}
|
||||
|
||||
static class ConfluenceSpaceResult {
|
||||
List<ConfluenceSpace> results
|
||||
}
|
||||
|
||||
static class ConfluenceBody {
|
||||
ConfluenceStorage storage
|
||||
}
|
||||
|
||||
static class ConfluenceStorage {
|
||||
String value
|
||||
String representation
|
||||
}
|
||||
|
||||
static class ConfluenceResponse {
|
||||
int statusCode
|
||||
String message
|
||||
Map<String, ?> data
|
||||
}
|
||||
|
||||
static interface ConfluenceApi {
|
||||
@GET("content")
|
||||
Call<ConfluenceContentResult> getContentResults();
|
||||
|
||||
@GET("content")
|
||||
Call<ConfluenceContentResult> getContentBySpaceKeyAndTitle(final @Query("key") String key, final @Query("title") String title);
|
||||
|
||||
@GET("content/{id}/child/{type}")
|
||||
Call<ConfluenceContentResult> getChildren(final @Path("id") String parentId, final @Path("type") String type, final @QueryMap Map<String, String> params);
|
||||
|
||||
|
||||
@GET("content/{id}?expand=body.storage")
|
||||
Call<ConfluenceContent> getContentById(final @Path("id") String id);
|
||||
|
||||
@POST("contentbody/convert/{to}")
|
||||
Call<ConfluenceStorage> postContentConversion(final @Body ConfluenceStorage storage, final @Path("to") String convertToFormat);
|
||||
|
||||
@POST("content")
|
||||
Call<ConfluenceContent> postContent(final @Body ConfluenceContent content);
|
||||
|
||||
@PUT("content/{id}")
|
||||
Call<ConfluenceContent> putContent(final @Path("id") String id, final @Body ConfluenceContent content);
|
||||
|
||||
@POST("content")
|
||||
void postContentWithCallback(final @Body ConfluenceContent content, final Callback<ConfluenceContent> callback);
|
||||
|
||||
@DELETE("content/{id}")
|
||||
Call<ConfluenceResponse> deleteContentById(final @Path("id") String id);
|
||||
|
||||
@GET("space")
|
||||
Call<ConfluenceSpaceResult> getSpaces();
|
||||
|
||||
@POST("space")
|
||||
Call<ConfluenceSpace> createSpace(final @Body ConfluenceSpace space);
|
||||
|
||||
@POST("space/_private")
|
||||
Call<ConfluenceSpace> createPrivateSpace(final @Body ConfluenceSpace space);
|
||||
|
||||
@GET("space/{spaceKey}/content/page")
|
||||
Call<ConfluenceContentResult> getAllSpaceContent(final @Path("spaceKey") String spaceKey, final @QueryMap Map<String, String> params);
|
||||
|
||||
@GET("space/{spaceKey}/content/{type}")
|
||||
Call<ConfluenceContentResult> getRootContentBySpaceKey(final @Path("spaceKey") String spaceKey, final @Path("type") String contentType);
|
||||
}
|
||||
}
|
||||
// curl -u aleks:xxx -X POST -H 'Content-Type: application/json' -d '{"type":"page","title":"1.7.0 - Apache Fineract","space":{"key":"FINERACT"},"ancestors":[{"id":75974324}],"body":{"storage":{"value":"<p>This is <br/> a new page</p>","representation": "storage"}}}' https://cwiki.apache.org/confluence/content/ | python -mjson.tool
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import org.apache.fineract.gradle.FineractPluginExtension
|
||||
import org.apache.fineract.gradle.FineractPluginExtension.FineractPluginEmailParams
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import javax.mail.*
|
||||
import javax.mail.internet.InternetAddress
|
||||
import javax.mail.internet.MimeMessage
|
||||
|
||||
class EmailService {
|
||||
private static final Logger log = LoggerFactory.getLogger(EmailService.class)
|
||||
|
||||
String username
|
||||
String password
|
||||
Properties properties
|
||||
|
||||
EmailService(FineractPluginExtension.FineractPluginConfigSmtp config) {
|
||||
this.username = config.username
|
||||
this.password = config.password
|
||||
|
||||
this.properties = new Properties()
|
||||
this.properties.put("mail.smtp.host", config.host)
|
||||
this.properties.put("mail.smtp.auth", "true")
|
||||
this.properties.put("mail.smtp.starttls.enable", config.tls.toString())
|
||||
if(config.ssl) {
|
||||
this.properties.put("mail.smtp.port", "465")
|
||||
this.properties.put("mail.smtp.socketFactory.port", "465");
|
||||
this.properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
|
||||
|
||||
} else {
|
||||
this.properties.put("mail.smtp.port", "587")
|
||||
}
|
||||
}
|
||||
|
||||
void send(FineractPluginEmailParams params) {
|
||||
log.warn("Sending email: ${params.from} -> ${params.to} - ${params.subject}")
|
||||
|
||||
Session session = Session.getInstance(this.properties, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(getUsername(), getPassword())
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
|
||||
Message msg = new MimeMessage(session)
|
||||
msg.setFrom(new InternetAddress(params.from, params.name))
|
||||
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(params.to))
|
||||
if(params.cc) {
|
||||
msg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(params.cc))
|
||||
}
|
||||
if(params.bcc) {
|
||||
msg.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(params.bcc))
|
||||
}
|
||||
msg.setSubject(params.subject)
|
||||
msg.setText(params.message);
|
||||
|
||||
Transport.send(msg);
|
||||
|
||||
log.info("Done")
|
||||
|
||||
} catch (MessagingException e) {
|
||||
log.error(e.toString(), e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import org.apache.fineract.gradle.FineractPluginExtension
|
||||
import org.eclipse.jgit.annotations.NonNull
|
||||
import org.eclipse.jgit.annotations.Nullable
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.api.errors.CanceledException
|
||||
import org.eclipse.jgit.lib.CommitBuilder
|
||||
import org.eclipse.jgit.lib.GpgSigner
|
||||
import org.eclipse.jgit.lib.PersonIdent
|
||||
import org.eclipse.jgit.lib.Repository
|
||||
import org.eclipse.jgit.lib.StoredConfig
|
||||
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
|
||||
import org.eclipse.jgit.transport.CredentialsProvider
|
||||
import org.eclipse.jgit.transport.RefSpec
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class GitService {
|
||||
private static final Logger log = LoggerFactory.getLogger(GitService.class)
|
||||
|
||||
private Git git
|
||||
private boolean dryRun
|
||||
|
||||
GitService(FineractPluginExtension.FineractPluginConfigGit config, FineractPluginExtension.FineractPluginConfigGpg gpgConfig) {
|
||||
GpgSigner.setDefault(new GpgSigner() {
|
||||
@Override
|
||||
void sign(@NonNull CommitBuilder commit, @Nullable String gpgSigningKey, @NonNull PersonIdent committer, CredentialsProvider credentialsProvider) throws CanceledException {
|
||||
log.warn("------------------------ KEY: ${gpgSigningKey} IDENT: ${committer}")
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canLocateSigningKey(@Nullable String gpgSigningKey, @NonNull PersonIdent committer, CredentialsProvider credentialsProvider) throws CanceledException {
|
||||
log.warn("------------------------ KEY: ${gpgSigningKey} IDENT: ${committer}")
|
||||
return false
|
||||
}
|
||||
});
|
||||
|
||||
Repository repository = new FileRepositoryBuilder()
|
||||
.readEnvironment()
|
||||
.setGitDir(new File(config.dir))
|
||||
.findGitDir()
|
||||
.build();
|
||||
|
||||
this.git = new Git(repository)
|
||||
this.dryRun = config.dryRun
|
||||
}
|
||||
|
||||
void clone(String url, String branch, String directory) {
|
||||
def ref = "refs/heads/" + branch
|
||||
|
||||
Git.cloneRepository()
|
||||
.setURI(url)
|
||||
.setDirectory(new File(directory))
|
||||
.setBranchesToClone(Arrays.asList(ref))
|
||||
.setBranch(ref)
|
||||
.call();
|
||||
}
|
||||
|
||||
void pull(String directory) {
|
||||
Git git = Git.open(new File(directory));
|
||||
|
||||
git.pull().call()
|
||||
}
|
||||
|
||||
void commit(String directory) {
|
||||
Git git = Git.open(new File(directory));
|
||||
|
||||
def status = git.status().call()
|
||||
|
||||
if(!status.isClean()) {
|
||||
git.add().addFilepattern(".").call()
|
||||
|
||||
git.commit().setSign(true).setMessage("chore: Publish current docs").call()
|
||||
}
|
||||
|
||||
def result = git.push().call()
|
||||
}
|
||||
|
||||
void config(String directory, List<FineractPluginExtension.FineractPluginConfigGitSection> sections) {
|
||||
Git git = Git.open(new File(directory));
|
||||
|
||||
StoredConfig gitConfig = git.getRepository().getConfig();
|
||||
|
||||
sections.forEach(section -> {
|
||||
gitConfig.setString(section.section, section.subsection, section.name, section.value);
|
||||
})
|
||||
// gitConfig.setString("branch", "master", "merge", "refs/heads/master");
|
||||
gitConfig.save();
|
||||
}
|
||||
|
||||
void createTag(String name, String message) {
|
||||
def ref = git.tag()
|
||||
.setName(name)
|
||||
.setMessage(message)
|
||||
.setAnnotated(true)
|
||||
.setForceUpdate(true)
|
||||
.call()
|
||||
|
||||
try {
|
||||
git.push().setPushTags().setForce(true).setDryRun(dryRun).call()
|
||||
|
||||
log.warn("Tag created: ${name} (${ref.name})")
|
||||
} catch(Exception e) {
|
||||
log.error(e.toString(), e)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: explicitly provide the directory
|
||||
void removeTag(String name) {
|
||||
git.tagDelete().setTags(name).call()
|
||||
|
||||
RefSpec refSpec = new RefSpec(":refs/tags/${name}");
|
||||
git.push().setRefSpecs(refSpec).setRemote("origin").call()
|
||||
|
||||
log.warn("Tag deleted: ${name}")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import org.apache.fineract.gradle.FineractPluginExtension
|
||||
import org.apache.fineract.gradle.FineractPluginExtension.FineractPluginConfigGpg
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream
|
||||
import org.bouncycastle.bcpg.BCPGOutputStream
|
||||
import org.bouncycastle.gpg.keybox.BlobType
|
||||
import org.bouncycastle.gpg.keybox.PublicKeyRingBlob
|
||||
import org.bouncycastle.gpg.keybox.bc.BcKeyBox
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.bouncycastle.openpgp.*
|
||||
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder
|
||||
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import java.security.GeneralSecurityException
|
||||
import java.security.MessageDigest
|
||||
import java.security.Security
|
||||
|
||||
class GpgService {
|
||||
private static final Logger log = LoggerFactory.getLogger(GpgService.class)
|
||||
|
||||
private PGPPrivateKey privateKey;
|
||||
private PGPPublicKey publicKey;
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
GpgService(FineractPluginConfigGpg config) {
|
||||
// TODO: provide implementation for *.gpg key rings
|
||||
def keyBox = config.publicKeyring.endsWith(".kbx") ? new BcKeyBox(new FileInputStream(config.publicKeyring)) : null
|
||||
|
||||
if(keyBox) {
|
||||
for(def keyBlob : keyBox.getKeyBlobs()) {
|
||||
switch (keyBlob.type) {
|
||||
case BlobType.X509_BLOB:
|
||||
break
|
||||
case BlobType.OPEN_PGP_BLOB:
|
||||
def publicKeyRing = (keyBlob as PublicKeyRingBlob).getPGPPublicKeyRing();
|
||||
def iterator = publicKeyRing.getPublicKeys();
|
||||
|
||||
while (publicKey == null && iterator.hasNext()) {
|
||||
def k = iterator.next();
|
||||
|
||||
if (k.isEncryptionKey()) {
|
||||
def keyName = Long.toHexString(k.keyID).toUpperCase()
|
||||
|
||||
if(config.keyName.substring(config.keyName.length()-keyName.length()) == keyName) {
|
||||
publicKey = k;
|
||||
log.warn("Found: ${publicKey.getUserIDs().next()}")
|
||||
break
|
||||
} else {
|
||||
log.warn("Key mismatch: ${config.keyName} <-> ${keyName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
PGPSecretKeyRingCollection secretRingCollection = new PGPSecretKeyRingCollection(new FileInputStream(config.secretKeyring), new BcKeyFingerprintCalculator())
|
||||
|
||||
def secretKey = secretRingCollection.getSecretKey(publicKey.keyID)
|
||||
|
||||
if(secretKey!=null) {
|
||||
this.privateKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(config.password.toCharArray()))
|
||||
}
|
||||
} else {
|
||||
log.warn("Could not open the public key ring.")
|
||||
}
|
||||
}
|
||||
|
||||
void sign(FineractPluginExtension.FineractPluginGpgParams params) {
|
||||
InputStream is = new FileInputStream(params.file)
|
||||
|
||||
BCPGOutputStream os = new BCPGOutputStream(new ArmoredOutputStream(new FileOutputStream(params.file + ".asc")))
|
||||
sign(is).encode(os)
|
||||
os.close();
|
||||
}
|
||||
|
||||
PGPSignature sign(InputStream is) throws IOException, PGPException, GeneralSecurityException {
|
||||
|
||||
PGPSignatureGenerator generator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(publicKey.getAlgorithm(), PGPUtil.SHA1));
|
||||
generator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
|
||||
|
||||
byte[] buf = new byte[4096];
|
||||
int len;
|
||||
|
||||
while ((len = is.read(buf)) >= 0) {
|
||||
generator.update(buf, 0, len);
|
||||
}
|
||||
|
||||
return generator.generate();
|
||||
}
|
||||
|
||||
|
||||
String md5(FineractPluginExtension.FineractPluginGpgParams params) {
|
||||
return calc(new FileInputStream(params.file), MessageDigest.getInstance("MD5", BouncyCastleProvider.PROVIDER_NAME))
|
||||
}
|
||||
|
||||
String sha512(FineractPluginExtension.FineractPluginGpgParams params) {
|
||||
return calc(new FileInputStream(params.file), MessageDigest.getInstance("SHA-512", BouncyCastleProvider.PROVIDER_NAME))
|
||||
}
|
||||
|
||||
private static String calc(InputStream is, MessageDigest digest) {
|
||||
String output
|
||||
int read
|
||||
byte[] buffer = new byte[8192]
|
||||
|
||||
try {
|
||||
while ((read = is.read(buffer)) > 0) {
|
||||
digest.update(buffer, 0, read)
|
||||
}
|
||||
byte[] hash = digest.digest()
|
||||
BigInteger bigInt = new BigInteger(1, hash)
|
||||
output = bigInt.toString(16)
|
||||
while ( output.length() < 32 ) {
|
||||
output = "0"+output
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(), e)
|
||||
return null
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import okhttp3.*
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.apache.fineract.gradle.FineractPluginExtension
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import retrofit2.Call
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory
|
||||
import retrofit2.http.*
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class JiraService {
|
||||
private static final Logger log = LoggerFactory.getLogger(JiraService.class)
|
||||
|
||||
private JiraApi api
|
||||
|
||||
JiraService(FineractPluginExtension.FineractPluginConfigJira config) {
|
||||
def credentials = Credentials.basic(config.username, config.password)
|
||||
|
||||
final ConnectionPool connectionPool = new ConnectionPool(5, 60, TimeUnit.SECONDS);
|
||||
|
||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(log::debug)
|
||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||
|
||||
OkHttpClient.Builder okClient = new OkHttpClient.Builder()
|
||||
.connectionPool(connectionPool)
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
|
||||
def request = chain.request()
|
||||
request = request.newBuilder().header("Authorization", credentials).build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
})
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
|
||||
Retrofit.Builder builder = new Retrofit.Builder()
|
||||
.baseUrl(config.url)
|
||||
.client(okClient.build())
|
||||
.addConverterFactory(JacksonConverterFactory.create(mapper))
|
||||
|
||||
Retrofit retrofit = builder.build()
|
||||
|
||||
this.api = retrofit.create(JiraApi.class)
|
||||
}
|
||||
|
||||
Map<String, Object> serverInfo() {
|
||||
return this.api.serverInfo.execute().body()
|
||||
}
|
||||
|
||||
FineractPluginExtension.FineractPluginJiraParams search(FineractPluginExtension.FineractPluginJiraParams params) {
|
||||
def query = [:]
|
||||
|
||||
query['jql'] = params.query
|
||||
query['startAt'] = params.pageOffset?:0
|
||||
query['maxResults'] = params.pageSize?:50
|
||||
// query['expand'] = [
|
||||
// "names",
|
||||
// "schema",
|
||||
// "operations"
|
||||
// ]
|
||||
// query['fields'] = params.fields?:"*all"
|
||||
query['fields'] = params.includes?:[
|
||||
"summary",
|
||||
"status",
|
||||
"assignee",
|
||||
"fixVersions"
|
||||
]
|
||||
|
||||
// params.total = params.total?:1000
|
||||
params.total = 1000
|
||||
|
||||
def result = this.api.searchIssues(query).execute().body()
|
||||
|
||||
params.result.addAll(result?.issues as List)
|
||||
|
||||
if(result?.issues.size()>=params.pageSize && params.result.size() < params.total) {
|
||||
params.pageOffset += params.pageSize
|
||||
search(params);
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
List getProjects() {
|
||||
return this.api.getProjects()?.execute()?.body()
|
||||
}
|
||||
|
||||
List getProjectVersions(String projectId) {
|
||||
return this.api.getProjectVersions(projectId)?.execute()?.body()
|
||||
}
|
||||
|
||||
JiraVersion getVersion(String id) {
|
||||
return this.api.getVersion(id)?.execute()?.body()
|
||||
}
|
||||
|
||||
void updateVersion(JiraVersion version) {
|
||||
this.api.updateVersion(version.id, version).execute()
|
||||
}
|
||||
|
||||
static class JiraVersion {
|
||||
String self
|
||||
String id
|
||||
String description
|
||||
String name
|
||||
boolean archived
|
||||
boolean released
|
||||
String releaseDate
|
||||
String userReleaseDate
|
||||
String project
|
||||
Integer projectId
|
||||
}
|
||||
|
||||
static class JiraSearchResult {
|
||||
String expand
|
||||
Integer startAt
|
||||
Integer maxResults
|
||||
Integer total
|
||||
List<JiraIssue> issues
|
||||
List<String> warningMessages
|
||||
Map<String, Object> names
|
||||
Map<String, Object> schema
|
||||
}
|
||||
|
||||
static class JiraIssue {
|
||||
String expand
|
||||
String self
|
||||
String id
|
||||
String key
|
||||
Map<String, Object> renderedFields
|
||||
Map<String, Object> properties
|
||||
Map<String, Object> names
|
||||
Map<String, Object> schema
|
||||
Map<String, Object> fields
|
||||
}
|
||||
|
||||
static class JiraIssueUpdateRequest {
|
||||
Map<String, Map<String, Object>> historyMetadata
|
||||
Map<String, List<Map<String, Object>>> update
|
||||
Map<String, Object> fields
|
||||
Map<String, List<Map<String, Object>>> properties
|
||||
}
|
||||
|
||||
static interface JiraApi {
|
||||
// Server Info
|
||||
@GET("serverInfo")
|
||||
Call<Map<String, Object>> getServerInfo()
|
||||
|
||||
// Components
|
||||
@GET("component/{id}")
|
||||
Call<Object> getComponent(@Path("id") String id)
|
||||
|
||||
@POST("component")
|
||||
Call<Object> createComponent(@Body Object component)
|
||||
|
||||
@PUT("component/{id}")
|
||||
Call<Void> updateComponent(@Path("id") String id, @Body Object component)
|
||||
|
||||
@GET("component/{id}/relatedIssueCounts")
|
||||
Call<Object> getComponentIssueCount(@Path("id") String id)
|
||||
|
||||
// Issue
|
||||
@GET("issue/{issueIdOrKey}")
|
||||
Call<Object> getIssue(@Path("issueIdOrKey") String issueIdOrKey)
|
||||
|
||||
@POST("issue")
|
||||
Call<Object> createIssue(@Body Object issue)
|
||||
|
||||
@PUT("issue/{issueIdOrKey}")
|
||||
Call<Object> updateIssue(@Path("issueIdOrKey") String issueIdOrKey, @Body Object issue, @QueryMap Map<String, String> queryMap)
|
||||
|
||||
@POST("issue/bulk")
|
||||
Call<Object> createIssues(@Body Object issues)
|
||||
|
||||
@PUT("issue/{issueIdOrKey}/assignee")
|
||||
Call<Void> assignIssue(@Path("issueIdOrKey") String issueIdOrKey, @Body Object user)
|
||||
|
||||
@GET("issue/{issueIdOrKey}/comment")
|
||||
Call<Object> getComments(@Path("issueIdOrKey") String issueIdOrKey)
|
||||
|
||||
@POST("issue/{issueIdOrKey}/comment")
|
||||
Call<Object> addComment(@Path("issueIdOrKey") String issueIdOrKey, @Body Object comment)
|
||||
|
||||
@PUT("issue/{issueIdOrKey}/comment/{id}")
|
||||
Call<Object> updateComment(@Path("issueIdOrKey") String issueIdOrKey, @Path("id") String id, @Body Object comment)
|
||||
|
||||
@GET("issue/{issueIdOrKey}/comment/{id}")
|
||||
Call<Object> getComment(@Path("issueIdOrKey") String issueIdOrKey, @Path("id") String id)
|
||||
|
||||
@POST("issue/{issueIdOrKey}/notify")
|
||||
Call<Void> notifyIssue(@Path("issueIdOrKey") String issueIdOrKey, @Body Object notify)
|
||||
|
||||
@GET("issue/{issueIdOrKey}/transitions?expand=transitions.fields")
|
||||
Call<Object> getTransitions(@Path("issueIdOrKey") String issueIdOrKey)
|
||||
|
||||
@POST("issue/{issueIdOrKey}/transitions")
|
||||
Call<Void> transitionIssue(@Path("issueIdOrKey") String issueIdOrKey, @Body Object issue)
|
||||
|
||||
@GET("issue/{issueIdOrKey}/watchers")
|
||||
Call<Object> getIssueWatches(@Path("issueIdOrKey") String issueIdOrKey)
|
||||
|
||||
@POST("issue/{issueIdOrKey}/watchers")
|
||||
Call<Void> addIssueWatcher(@Path("issueIdOrKey") String issueIdOrKey, @Body String user)
|
||||
|
||||
// Remote Issue Links
|
||||
@GET("issue/{issueIdOrKey}/remotelink")
|
||||
Call<Object> getIssueRemoteLinks(@Path("issueIdOrKey") String issueIdOrKey, @Query("globalId") String globalId)
|
||||
|
||||
@GET("issue/{issueIdOrKey}/remotelink/{linkId}")
|
||||
Call<Object> getIssueRemoteLink(@Path("issueIdOrKey") String issueIdOrKey, @Path("linkId") String linkId)
|
||||
|
||||
@POST("issue/{issueIdOrKey}/remotelink")
|
||||
Call<Object> createIssueRemoteLink(@Path("issueIdOrKey") String issueIdOrKey, @Body Object issueLink)
|
||||
|
||||
@POST("issue/{issueIdOrKey}/remotelink/{linkId}")
|
||||
Call<Object> updateIssueRemoteLink(@Path("issueIdOrKey") String issueIdOrKey, @Path("linkId") String linkId, @Body Object issueLink)
|
||||
|
||||
@DELETE("issue/{issueIdOrKey}/remotelink")
|
||||
Call<Object> deleteIssueRemoteLinks(@Path("issueIdOrKey") String issueIdOrKey, @Query("globalId") String globalId)
|
||||
|
||||
@DELETE("issue/{issueIdOrKey}/remotelink/{linkId}")
|
||||
Call<Object> deleteIssueRemoteLink(@Path("issueIdOrKey") String issueIdOrKey, @Path("linkId") String linkId)
|
||||
|
||||
// Issue Links
|
||||
@POST("issueLink")
|
||||
Call<Void> createIssueLink(@Body Object issueLink)
|
||||
|
||||
@GET("issueLink/{linkId}")
|
||||
Call<Object> getIssueLink(@Path("linkId") String linkId)
|
||||
|
||||
@DELETE("issueLink/{linkId}")
|
||||
Call<Object> deleteIssueLink(@Path("linkId") String linkId)
|
||||
|
||||
// Issue Link Types
|
||||
@GET("issueLinkType")
|
||||
Call<Object> getIssueLinkTypes()
|
||||
|
||||
// Project
|
||||
@GET("project?expand=lead,description")
|
||||
Call<List> getProjects()
|
||||
|
||||
@GET("project/{projectIdOrKey}")
|
||||
Call<Object> getProject(@Path("projectIdOrKey") String projectId)
|
||||
|
||||
@GET("project/{projectIdOrKey}/statuses")
|
||||
Call<Object> getProjectStatuses(@Path("projectIdOrKey") String projectId)
|
||||
|
||||
@GET("project/{projectIdOrKey}/components")
|
||||
Call<Object> getProjectComponents(@Path("projectIdOrKey") String projectId)
|
||||
|
||||
@GET("project/{projectIdOrKey}/versions")
|
||||
Call<List<JiraVersion>> getProjectVersions(@Path("projectIdOrKey") String projectId)
|
||||
|
||||
// Search
|
||||
@POST("search")
|
||||
Call<JiraSearchResult> searchIssues(@Body Map<String, Object> search)
|
||||
|
||||
// Version
|
||||
@POST("version")
|
||||
Call<JiraVersion> createVersion(@Body JiraVersion version)
|
||||
|
||||
@GET("version/{id}")
|
||||
Call<JiraVersion> getVersion(@Path("id") String id)
|
||||
|
||||
@PUT("version/{id}")
|
||||
Call<Void> updateVersion(@Path("id") String id, @Body JiraVersion version)
|
||||
|
||||
// Fields
|
||||
@GET("field")
|
||||
Call<Object> getFields()
|
||||
|
||||
@POST("field")
|
||||
Call<Object> createField(@Body Object field)
|
||||
|
||||
// Users
|
||||
@GET("user/search")
|
||||
Call<Object> userSearch(@Query("username") String userName, @Query("startAt") int startAt, @Query("maxResults") int maxResults)
|
||||
|
||||
@GET("user/assignable/search")
|
||||
Call<Object> assignableUserSearch(@Query("username") String userName, @Query("project") String project, @Query("issueKey") String issueKey, @Query("startAt") int startAt, @Query("maxResults") int maxResults)
|
||||
|
||||
@Multipart
|
||||
@retrofit2.http.Headers("X-Atlassian-Token: no-check")
|
||||
@POST("issue/{issueIdOrKey}/attachments")
|
||||
Call<Object> uploadAttachment(@Path("issueIdOrKey") String issueIdOrKey, @Part MultipartBody.Part file)
|
||||
|
||||
@GET("attachment/{attachmentId}")
|
||||
Call<Object> getAttachment(@Path("attachmentId") String attachmentId)
|
||||
|
||||
@DELETE("attachment/{attachmentId}")
|
||||
Call<Object> deleteAttachment(@Path("attachmentId") String attachmentId)
|
||||
|
||||
@Streaming
|
||||
@GET
|
||||
Call<ResponseBody> downloadFileWithDynamicUrl(@Url String fileUrl)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import org.apache.fineract.gradle.FineractPluginExtension
|
||||
import org.gradle.api.GradleException
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.tmatesoft.svn.core.SVNCancelException
|
||||
import org.tmatesoft.svn.core.SVNDepth
|
||||
import org.tmatesoft.svn.core.SVNException
|
||||
import org.tmatesoft.svn.core.SVNURL
|
||||
import org.tmatesoft.svn.core.wc.*
|
||||
|
||||
class SubversionService {
|
||||
private static final Logger log = LoggerFactory.getLogger(SubversionService.class)
|
||||
|
||||
private SVNUpdateClient client;
|
||||
private String revision;
|
||||
|
||||
SubversionService(FineractPluginExtension.FineractPluginConfigSubversion config) {
|
||||
ISVNOptions options = SVNWCUtil.createDefaultOptions(true)
|
||||
SVNClientManager clientManager = SVNClientManager.newInstance(options, SVNWCUtil.createDefaultAuthenticationManager(config.username, config.password))
|
||||
|
||||
this.client = clientManager.updateClient;
|
||||
this.client.ignoreExternals = false
|
||||
this.client.eventHandler = new ISVNEventHandler() {
|
||||
@Override
|
||||
void handleEvent(SVNEvent event, double progress) throws SVNException {
|
||||
println ">>> PROGRESS: ${progress>0?:'-'}"
|
||||
}
|
||||
|
||||
@Override
|
||||
void checkCancelled() throws SVNCancelException {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.revision = config.revision
|
||||
}
|
||||
|
||||
void checkout(FineractPluginExtension.FineractPluginSubversionParams params) {
|
||||
SVNURL svnUrl = SVNURL.parseURIEncoded(params.url)
|
||||
|
||||
SVNRevision svnRevision = this.revision == null ? SVNRevision.HEAD : SVNRevision.parse(this.revision)
|
||||
|
||||
if (svnRevision == SVNRevision.UNDEFINED) {
|
||||
throw new GradleException("Invalid SVN revision: " + this.revision)
|
||||
}
|
||||
|
||||
this.client.doCheckout(svnUrl, new File(params.directory), SVNRevision.HEAD, svnRevision, SVNDepth.INFINITY, false)
|
||||
}
|
||||
|
||||
void commit(FineractPluginExtension.FineractPluginSubversionParams params) {
|
||||
// TODO: implement this
|
||||
SVNURL svnUrl = SVNURL.parseURIEncoded(params.url)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.gradle.service
|
||||
|
||||
import freemarker.core.Environment
|
||||
import freemarker.template.Configuration
|
||||
import freemarker.template.Template
|
||||
import freemarker.template.TemplateException
|
||||
import freemarker.template.TemplateExceptionHandler
|
||||
import org.apache.fineract.gradle.FineractPluginExtension
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class TemplateService {
|
||||
private static final Logger log = LoggerFactory.getLogger(TemplateService.class)
|
||||
|
||||
private Configuration config
|
||||
|
||||
TemplateService(FineractPluginExtension.FineractPluginConfigTemplate config) {
|
||||
def dir = new File(config.templateDir);
|
||||
|
||||
this.config = new Configuration(Configuration.VERSION_2_3_31);
|
||||
this.config.setDirectoryForTemplateLoading(dir)
|
||||
this.config.setDefaultEncoding("UTF-8");
|
||||
this.config.setLogTemplateExceptions(false);
|
||||
this.config.setWrapUncheckedExceptions(true);
|
||||
this.config.setFallbackOnNullLoopVariable(false);
|
||||
this.config.setTemplateExceptionHandler(new TemplateExceptionHandler() {
|
||||
@Override
|
||||
void handleTemplateException(TemplateException te, Environment env, Writer out) throws TemplateException {
|
||||
if (!env.isInAttemptBlock()) {
|
||||
PrintWriter pw = (out instanceof PrintWriter) ? (PrintWriter) out : new PrintWriter(out);
|
||||
pw.print("FreeMarker template error\n");
|
||||
te.printStackTrace(pw, false, true, false);
|
||||
pw.flush(); // To commit the HTTP response
|
||||
}
|
||||
}}
|
||||
)
|
||||
}
|
||||
|
||||
FineractPluginExtension.FineractPluginTemplateParams render(FineractPluginExtension.FineractPluginTemplateParams params, Object data) {
|
||||
Template template = null;
|
||||
|
||||
if(params.templateFile) {
|
||||
template = new Template("template", new FileReader(new File(params.templateFile)), this.config)
|
||||
}
|
||||
if(params.template) {
|
||||
template = new Template("template", new StringReader(params.template), this.config)
|
||||
}
|
||||
|
||||
if(template) {
|
||||
if(params.outputFile) {
|
||||
def output = new File(params.outputFile)
|
||||
output.createNewFile()
|
||||
|
||||
template.process(data, new FileWriter(output, false))
|
||||
} else {
|
||||
def output = new StringWriter()
|
||||
|
||||
template.process(data, output)
|
||||
|
||||
params.output = output.toString()
|
||||
}
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
implementation-class=org.apache.fineract.gradle.FineractPlugin
|
||||
@@ -0,0 +1,34 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<h1>Release Artifacts</h1>
|
||||
<p>
|
||||
<ac:structured-macro ac:macro-id="ac1cb3cb-84d4-45aa-820b-05cc42d39dd9" ac:name="jira" ac:schema-version="1">
|
||||
<ac:parameter ac:name="server">ASF JIRA</ac:parameter>
|
||||
<ac:parameter ac:name="columnIds">issuekey,summary,issuetype,created,updated,duedate,assignee,reporter,customfield_12311032,customfield_12311037,customfield_12311022,customfield_12311027,priority,status,resolution</ac:parameter>
|
||||
<ac:parameter ac:name="columns">key,summary,type,created,updated,due,assignee,reporter,Priority,Priority,Priority,Priority,priority,status,resolution</ac:parameter>
|
||||
<ac:parameter ac:name="maximumIssues">50</ac:parameter>
|
||||
<ac:parameter ac:name="jqlQuery">project = FINERACT AND fixVersion = ${project['fineract.release.version']} AND status not in (Open) </ac:parameter>
|
||||
<ac:parameter ac:name="serverId">5aa69414-a9e9-3523-82ec-879b028fb15b</ac:parameter>
|
||||
</ac:structured-macro>
|
||||
</p>
|
||||
<p>
|
||||
<br/>
|
||||
</p>
|
||||
@@ -0,0 +1,39 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
Hello everyone,
|
||||
|
||||
... based on our "How to Release Apache Fineract" process documented at https://cwiki.apache.org/confluence/x/DRwIB:
|
||||
|
||||
I will create a ${project['fineract.release.version']} branch off develop in our git repository at https://github.com/apache/fineract on ${project['fineract.release.date']}.
|
||||
|
||||
The release tracking umbrella issue for tracking all activity in JIRA is FINERACT-${project['fineract.release.issue']!'0000'} (https://issues.apache.org/jira/browse/FINERACT-${project['fineract.release.issue']!'0000'}) for this Fineract ${project['fineract.release.version']}.
|
||||
|
||||
If you have any work in progress that you would like to see included in this release, please add "blocking" links to the release JIRA issue.
|
||||
|
||||
I am the release manager for this release.
|
||||
|
||||
Cheers,
|
||||
|
||||
${project['fineract.config.name']}
|
||||
|
||||
|
||||
|
||||
🎉 Powered by Fineract Release Plugin 🎊
|
||||
@@ -0,0 +1,21 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
[FINERACT] [PROPOSAL] 📦 New release ${project['fineract.release.version']}
|
||||
@@ -0,0 +1,35 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
Hello everyone,
|
||||
|
||||
... as previously announced, I've just created the release branch for our upcoming ${project['fineract.release.version']} release.
|
||||
|
||||
You can continue working and merging PRs to the develop branch for future releases, as always.
|
||||
|
||||
The DRAFT release notes are on https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract. Does anyone see anything missing?
|
||||
|
||||
Does anyone have any last minutes changes they would like to see cherry-picked to branch ${project['fineract.release.version']}, or are we good go and actually cut the release based on this branch as it is?
|
||||
|
||||
I'll initiate the final stage of actually creating the release on ${project['fineract.release.date']} if nobody objects.
|
||||
|
||||
Cheers,
|
||||
|
||||
${project['fineract.config.name']}
|
||||
@@ -0,0 +1,21 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
[FINERACT] [ANNOUNCE] 🔀 ${project['fineract.release.version']} release branch
|
||||
@@ -0,0 +1,43 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
Hello everyone,
|
||||
|
||||
... we have created Apache Fineract ${project['fineract.release.version']} release, with the artifacts below up for a vote.
|
||||
|
||||
It fixes the following issues: https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract
|
||||
|
||||
Source & Binary files : https://dist.apache.org/repos/dist/dev/fineract/${project['fineract.release.version']}/
|
||||
|
||||
Tag to be voted on (rc#): https://gitbox.apache.org/repos/asf?p=fineract.git;a=commit;h=refs/heads/${project['fineract.release.version']}
|
||||
|
||||
Fineract's KEYS containing the PGP key we used to sign the release: https://dist.apache.org/repos/dist/dev/fineract/KEYS
|
||||
|
||||
Note that this release contains source and binary artifacts.
|
||||
|
||||
This vote will be open for 72 hours:
|
||||
|
||||
[ ] +1 approve
|
||||
[ ] +0 no opinion
|
||||
[ ] -1 disapprove (and reason why)
|
||||
|
||||
Cheers,
|
||||
|
||||
${project['fineract.config.name']}
|
||||
@@ -0,0 +1,21 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
[FINERACT] [VOTE] 🗳️ ${project['fineract.release.version']} for release
|
||||
@@ -0,0 +1,89 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<#if (project['fineract.vote'].approve.binding?size + project['fineract.vote'].approve.nonBinding?size > project['fineract.vote'].disapprove.binding?size + project['fineract.vote'].disapprove.nonBinding?size)>
|
||||
Voting is now closed and has passed with the following tally,
|
||||
|
||||
Binding +1s: ${project['fineract.vote'].approve.binding?size}
|
||||
Non binding +1s: ${project['fineract.vote'].approve.nonBinding?size}
|
||||
<#else>
|
||||
Voting is now closed and has not passed with the following tally,
|
||||
|
||||
Binding +1s: ${project['fineract.vote'].approve.binding?size}
|
||||
Non binding +1s: ${project['fineract.vote'].approve.nonBinding?size}
|
||||
|
||||
Binding -1s: ${project['fineract.vote'].disapprove.binding?size}
|
||||
Non binding -1s: ${project['fineract.vote'].disapprove.nonBinding?size}
|
||||
</#if>
|
||||
|
||||
Here are the detailed results:
|
||||
|
||||
<#list project['fineract.vote'].approve.binding>
|
||||
Binding +1s:
|
||||
<#items as item>
|
||||
- ${item.name} (${item.email})
|
||||
</#items>
|
||||
</#list>
|
||||
|
||||
|
||||
<#list project['fineract.vote'].approve.nonBinding>
|
||||
Non binding +1s:
|
||||
<#items as item>
|
||||
- ${item.name} (${item.email})
|
||||
</#items>
|
||||
</#list>
|
||||
|
||||
|
||||
<#list project['fineract.vote'].disapprove.binding>
|
||||
Binding -1s:
|
||||
<#items as item>
|
||||
- ${item.name} (${item.email})
|
||||
</#items>
|
||||
</#list>
|
||||
|
||||
<#list project['fineract.vote'].disapprove.nonBinding>
|
||||
Non binding -1s:
|
||||
<#items as item>
|
||||
- ${item.name} (${item.email})
|
||||
</#items>
|
||||
</#list>
|
||||
|
||||
|
||||
<#list project['fineract.vote'].noOpinion.binding>
|
||||
Binding +0s:
|
||||
<#items as item>
|
||||
- ${item.name} (${item.email})
|
||||
</#items>
|
||||
</#list>
|
||||
|
||||
<#list project['fineract.vote'].noOpinion.nonBinding>
|
||||
Non binding +0s:
|
||||
<#items as item>
|
||||
- ${item.name} (${item.email})
|
||||
</#items>
|
||||
</#list>
|
||||
|
||||
<#if (project['fineract.vote'].approve.binding?size + project['fineract.vote'].approve.nonBinding?size > project['fineract.vote'].disapprove.binding?size + project['fineract.vote'].disapprove.nonBinding?size)>
|
||||
Thanks to everyone who voted! I'll now continue with the rest of the release process.
|
||||
<#else>
|
||||
Thanks to everyone who voted! Looks like we have to repeat the vote.
|
||||
</#if>
|
||||
|
||||
${project['fineract.config.name']}
|
||||
@@ -0,0 +1,21 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
[FINERACT] [VOTE] [RESULT] 🧾️ ${project['fineract.release.version']} for release
|
||||
@@ -0,0 +1,43 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
The Apache Fineract project is pleased to announce
|
||||
the release of Apache Fineract ${project['fineract.release.version']}.
|
||||
The release is available for download from
|
||||
https://fineract.apache.org/#downloads
|
||||
|
||||
Fineract provides a reliable, robust, and affordable solution for entrepreneurs,
|
||||
financial institutions, and service providers to offer financial services to the
|
||||
world’s 2 billion underbanked and unbanked. Fineract is aimed at innovative mobile
|
||||
and cloud-based solutions, and enables digital transaction accounts for all.
|
||||
|
||||
This release addressed ${project['fineract.release.issues']?size} issues.
|
||||
|
||||
Readme: https://github.com/apache/fineract/blob/${project['fineract.release.version']}/README.md
|
||||
|
||||
Release page: https://cwiki.apache.org/confluence/display/FINERACT/${project['fineract.release.version']}+-+Apache+Fineract
|
||||
|
||||
List of fixed issues:
|
||||
https://issues.apache.org/jira/secure/ReleaseNote.jspa?version=${project['fineract.release.versionId']}&styleName=Html&projectId=${project['fineract.release.projectId']}
|
||||
|
||||
For more information on Apache Fineract please visit
|
||||
project home page: https://fineract.apache.org
|
||||
|
||||
The Apache Fineract Team
|
||||
@@ -0,0 +1,21 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
[ANNOUNCE] Apache Fineract ${project['fineract.release.version']} Release
|
||||
@@ -0,0 +1,21 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
Fineract ${project['fineract.release.version']} release
|
||||
@@ -0,0 +1,38 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
[INSTRUCTIONS:START]
|
||||
|
||||
As discussed in https://issues.apache.org/jira/browse/FINERACT-1154, now that everything is final, please do the following to remove the release branch (and just keep the tag),
|
||||
and make sure that everything on the release tag is merged to develop and that e.g. git describe works:
|
||||
|
||||
>> git checkout develop
|
||||
>> git branch -D ${project['fineract.release.version']}
|
||||
>> git push origin :${project['fineract.release.version']}
|
||||
>> git checkout develop
|
||||
>> git checkout -b merge-${project['fineract.release.version']}
|
||||
>> git merge -s recursive -Xignore-all-space ${project['fineract.release.version']}
|
||||
>> git commit
|
||||
>> git push <USER>
|
||||
>> hub pull-request
|
||||
|
||||
[INSTRUCTIONS:END]
|
||||
@@ -0,0 +1,28 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
[INSTRUCTIONS:START]
|
||||
|
||||
Finally update the https://fineract.apache.org website with the latest release details.
|
||||
The website's HTML source code is available at https://github.com/apache/fineract-site.
|
||||
|
||||
[INSTRUCTIONS:END]
|
||||
@@ -0,0 +1,39 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
[INSTRUCTIONS:START]
|
||||
|
||||
Before a release is done, make sure that any issues that are fixed have their fix version setup correctly.
|
||||
|
||||
"project = FINERACT and resolution = fixed and fixVersion is empty"
|
||||
|
||||
Move all unresolved JIRA issues which have this release as Fix Version to the next release
|
||||
|
||||
"project = FINERACT and fixVersion = ${project['fineract.release.version']} and status not in ( Resolved, Done, Accepted, Closed )"
|
||||
|
||||
You can also run the following query to make sure that the issues fixed for the to-be-released version look accurate:
|
||||
|
||||
"project = FINERACT and fixVersion = ${project['fineract.release.version']}"
|
||||
|
||||
Finally, check out the output of the JIRA release note tool to see which tickets are included in the release, in order to do a sanity check.
|
||||
|
||||
[INSTRUCTIONS:END]
|
||||
@@ -0,0 +1,29 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
[INSTRUCTIONS:START]
|
||||
|
||||
You first need to close the release in JIRA so that the about to be released version cannot be used as "fixVersion" for new bugs anymore.
|
||||
Go to JIRA "Administer project" page and follow "Versions" in left menu. Table with list of all releases should appear, click on additional menu on the right of your release and choose "Release" option.
|
||||
Submit release date and you're done.
|
||||
|
||||
[INSTRUCTIONS:END]
|
||||
@@ -0,0 +1,38 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
[INSTRUCTIONS:START]
|
||||
|
||||
Following are the typical things we need to verify before voting on a release candidate. And the release manager should verify them too before calling out a vote.
|
||||
|
||||
Make sure release artifacts are hosted at https://dist.apache.org/repos/dist/dev/fineract
|
||||
|
||||
* Release candidates should be in format apache-fineract-${project['fineract.release.version']}-binary.tar.gz
|
||||
* Verify signatures and hashes. You may have to import the public key of the release manager to verify the signatures. (`gpg --recv-key <key id>`)
|
||||
* Git tag matches the released bits (diff -rf)
|
||||
* Can compile successfully from source
|
||||
* Verify DISCLAIMER, NOTICE and LICENSE (year etc)
|
||||
* All files have correct headers (Rat check should be clean - gradlew rat)
|
||||
* No jar files in the source artifacts
|
||||
* Integration tests should work
|
||||
|
||||
[INSTRUCTIONS:END]
|
||||
@@ -0,0 +1,33 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
= Changelog
|
||||
|
||||
== Breaking Changes
|
||||
|
||||
<#list issues?filter(i -> i.fields.labels?seq_contains("BreakingChange")) as issue>
|
||||
* [[${issue.key}]]${issue.key} - ${issue.fields.summary}
|
||||
</#list>
|
||||
|
||||
== Issues
|
||||
|
||||
<#list issues as issue>
|
||||
* [[${issue.key}]]${issue.key} - ${issue.fields.summary}
|
||||
</#list>
|
||||
@@ -0,0 +1,26 @@
|
||||
<#--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
Changelog
|
||||
=========
|
||||
|
||||
<#list issues as issue>
|
||||
* ${issue.key} - ${issue.fields.summary}
|
||||
</#list>
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"approve": {
|
||||
"binding": [
|
||||
{
|
||||
"name": "Some Body",
|
||||
"email": "bob@apache.org"
|
||||
}
|
||||
]
|
||||
},
|
||||
"disapprove": {
|
||||
"binding": [
|
||||
],
|
||||
"nonBinding": [
|
||||
]
|
||||
},
|
||||
"noOpinion": {
|
||||
"binding": [
|
||||
],
|
||||
"nonBinding": [
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -87,12 +87,27 @@ task buildTypescriptAngularSdk(type: org.openapitools.generator.gradle.plugin.ta
|
||||
]
|
||||
}
|
||||
|
||||
licenseFormatMain.dependsOn buildJavaSdk
|
||||
licenseFormatMain.dependsOn buildTypescriptAngularSdk
|
||||
compileJava.dependsOn licenseFormatMain
|
||||
task buildAsciidoc(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask){
|
||||
generatorName = 'asciidoc'
|
||||
verbose = false
|
||||
validateSpec = false
|
||||
skipValidateSpec = true
|
||||
inputSpec = "file:///$swaggerFile"
|
||||
outputDir = "$buildDir/generated/asciidoc".toString()
|
||||
apiPackage = 'org.apache.fineract.client.services'
|
||||
invokerPackage = 'org.apache.fineract.client'
|
||||
modelPackage = 'org.apache.fineract.client.models'
|
||||
dependsOn = [
|
||||
':fineract-provider:resolve'
|
||||
]
|
||||
}
|
||||
|
||||
// TODO: @vidakovic we could provide even more client libs in different languages (Go, Ruby, Swift etc.)
|
||||
compileJava.dependsOn buildJavaSdk, buildTypescriptAngularSdk, spotlessMiscApply
|
||||
licenseMain.dependsOn(buildJavaSdk, licenseFormatMain, spotlessMiscApply)
|
||||
licenseMain.dependsOn(buildTypescriptAngularSdk, licenseFormatMain, spotlessMiscApply)
|
||||
licenseMain.dependsOn(buildAsciidoc, licenseFormatMain, spotlessMiscApply)
|
||||
|
||||
compileJava.dependsOn(licenseFormatMain, spotlessMiscApply)
|
||||
|
||||
java {
|
||||
// keep this at Java 8, not 17; see https://issues.apache.org/jira/browse/FINERACT-1214
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
:years: 2015-2022
|
||||
:revnumber: 1.0.0
|
||||
:revnumber: 1.7.0
|
||||
:draft: true
|
||||
:rootdir: ../../../..
|
||||
:generated: ../../../build/generated/asciidoc
|
||||
:rootdir: {asciidoctorconfigdir}/..
|
||||
:generated: {asciidoctorconfigdir}/build/generated/asciidoc
|
||||
:diagramsdir: {asciidoctorconfigdir}/src/docs/en/diagrams
|
||||
:imagesdir: {asciidoctorconfigdir}/src/docs/en/images
|
||||
:icons: font
|
||||
|
||||
+21
-12
@@ -26,14 +26,14 @@ asciidoctorj {
|
||||
version = '2.5.3'
|
||||
|
||||
attributes = [
|
||||
version: "$project.version",
|
||||
version: "${project.version}",
|
||||
generated: "${buildDir}/generated/asciidoc",
|
||||
imagesdir: 'images',
|
||||
diagrams: 'diagrams',
|
||||
imagesdir: "${projectDir}/src/docs/en/images",
|
||||
diagramsdir: "${projectDir}/src/docs/en/diagrams",
|
||||
years: '2015-2022',
|
||||
revnumber: "$project.version".toString(),
|
||||
rootdir: "$rootDir".toString(),
|
||||
baseurl: 'fineract.apache.org'
|
||||
revnumber: "${project.version}".toString(),
|
||||
rootdir: "${rootDir}".toString(),
|
||||
baseurl: 'fineract.apache.org',
|
||||
]
|
||||
|
||||
modules {
|
||||
@@ -56,6 +56,8 @@ asciidoctor {
|
||||
include('index.adoc')
|
||||
}
|
||||
outputDir 'build/docs/html'
|
||||
|
||||
dependsOn ':fineract-client:buildAsciidoc'
|
||||
}
|
||||
|
||||
// asciidoctorEpub {
|
||||
@@ -68,6 +70,8 @@ asciidoctor {
|
||||
// }
|
||||
// outputDir 'build/docs/epub'
|
||||
// ebookFormats 'epub3'
|
||||
//
|
||||
// dependsOn ':fineract-client:buildAsciidoc'
|
||||
// }
|
||||
|
||||
asciidoctorPdf {
|
||||
@@ -88,21 +92,26 @@ asciidoctorPdf {
|
||||
// }
|
||||
// }
|
||||
// fontsDir "$projectDir/src/resources/fonts"
|
||||
dependsOn ':fineract-client:buildAsciidoc'
|
||||
}
|
||||
|
||||
task doc(type: Zip) {
|
||||
dependsOn asciidoctor, asciidoctorPdf
|
||||
|
||||
into("$project.name-$project.version") {
|
||||
into("fineract-${project.version}/html") {
|
||||
from("$buildDir/docs/html") {
|
||||
include "**/*"
|
||||
exclude "**/.asciidoctor"
|
||||
}
|
||||
}
|
||||
from ("$buildDir/docs/pdf") {
|
||||
include "index.pdf"
|
||||
rename {
|
||||
"$project.name-$project.version\\.pdf"
|
||||
into("fineract-${project.version}/pdf") {
|
||||
from("$buildDir/docs/pdf") {
|
||||
include "**/index.pdf"
|
||||
exclude "**/.asciidoctor"
|
||||
rename {
|
||||
"fineract-${project.version}.pdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
archiveName "$project.name-$project.version\\.zip"
|
||||
archiveName "${project.name}-${project.version}.zip"
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[appendix]
|
||||
@@ -1,272 +0,0 @@
|
||||
== Fineract Platform Software Architecture Document
|
||||
|
||||
=== Summary
|
||||
|
||||
This document captures the major architectural decisions in platform. The purpose of the document is to provide a guide to the overall structure of the platform; where it fits in the overall context of an MIS solution and its internals so that contributors can more effectively understand how changes that they are considering can be made, and the consequences of those changes.
|
||||
|
||||
The target audience for this report is both system integrators (who will use the document to gain an understanding of the structure of the platform and its design rationale) and platform contributors who will use the document to reason about future changes and who will update the document as the system evolves.
|
||||
|
||||
=== Introduction
|
||||
|
||||
==== The Idea
|
||||
|
||||
Fineract was an idea born out of a wish to create and deploy technology that allows the microfinance industry to scale. The goal is to:
|
||||
|
||||
- Produce a gold standard management information system suitable for microfinance operations
|
||||
- Acts as the basis of a platform for microfinance
|
||||
- Open source, owned and driven by member organisations in the community
|
||||
- Enabling potential for eco-system of providers located near to MFIs
|
||||
|
||||
==== History
|
||||
|
||||
- 2006: Project intiated by Grameen Foundation
|
||||
- Late 2011: Grameen Foundation handed over full responsibility to open source community.
|
||||
- 2012: Mifos X platform started. Previous members of project come together under the name of Community for Open Source Microfinance (COSM / OpenMF)
|
||||
- 2013: COSM / OpenMF officially rebranded to Mifos Initiative and receive US 501c3 status.
|
||||
- 2016: Fineract 1.x began incubation at Apache
|
||||
|
||||
==== Project Related
|
||||
|
||||
- Project url is https://github.com/apache/fineract
|
||||
- Issue tracker is https://issues.apache.org/jira/projects/FINERACT/summary
|
||||
- Download from http://fineract.apache.org/
|
||||
|
||||
=== System Overview
|
||||
|
||||
.Platform System Overview
|
||||
image::platform-systemview.png[]
|
||||
|
||||
Financial institutions deliver their services to customers through a variety of means today.
|
||||
|
||||
- Customers can call direct into branches (teller model)
|
||||
- Customers can organise into groups (or centers) and agree to meetup at a location and time with FI staff (traditional microfinance).
|
||||
- An FI might have a public facing information portal that customers can use for variety of reasons including account management (online banking).
|
||||
- An FI might be integrated into a ATM/POS/Card services network that the customer can use.
|
||||
- An FI might be integrated with a mobile money operator and support mobile money services for customer (present/future microfinance).
|
||||
- An FI might use third party agents to sell on products/services from other banks/FIs.
|
||||
|
||||
As illustrated in the above diagram, the various stakeholders leverage business apps to perform specific customer or FI related actions. The functionality contained in these business apps can be bundled up and packaged in any way. In the diagram, several of the apps may be combined into one app or any one of the blocks representing an app could be further broken up as needed.
|
||||
|
||||
The platform is the core engine of the MIS. It hides alot of the complexity that exists in the business and technical domains needed for an MIS in FIs behind a relatively simple API. It is this API that frees up app developers to innovate and produce apps that can be as general or as bespoke as FIs need them to be.
|
||||
|
||||
=== Functional Overview
|
||||
|
||||
As ALL capabilities of the platform are exposed through an API, The API docs are the best place to view a detailed breakdown of what the platform does. See online API Documentation.
|
||||
|
||||
.Platform Functional Overview
|
||||
image::platform-categories.png[]
|
||||
|
||||
At a higher level though we see the capabilities fall into the following categories:
|
||||
|
||||
* Infrastructure
|
||||
** Codes
|
||||
** Extensible Data Tables
|
||||
** Reporting
|
||||
|
||||
* User Administration
|
||||
** Users
|
||||
** Roles
|
||||
** Permissions
|
||||
|
||||
* Organisation Modelling
|
||||
** Offices
|
||||
** Staff
|
||||
** Currency
|
||||
|
||||
* Product Configuration
|
||||
** Charges
|
||||
** Loan Products
|
||||
** Deposit Products
|
||||
|
||||
* Client Data
|
||||
** Know Your Client (KYC)
|
||||
|
||||
* Portfolio Management
|
||||
** Loan Accounts
|
||||
** Deposit Accounts
|
||||
** Client/Groups
|
||||
|
||||
* GL Account Management
|
||||
** Chart of Accounts
|
||||
** General Ledger
|
||||
|
||||
=== Technology
|
||||
|
||||
* Java: http://www.oracle.com/technetwork/java/javase/downloads/index.html
|
||||
* JAX-RS using Jersey
|
||||
* JSON using Google GSON
|
||||
* Spring I/O Platform: http://spring.io/platform
|
||||
** Spring Framework
|
||||
** Spring Boot
|
||||
** Spring Security
|
||||
** Spring Data (JPA) backed by EclipseLink
|
||||
* MySQL: http://www.oracle.com/us/products/mysql/overview/index.html
|
||||
* PostgreSQL
|
||||
|
||||
=== Principles
|
||||
|
||||
==== RESTful API
|
||||
|
||||
The platform exposes all its functionality via a *practically-RESTful API*, that communicates using JSON.
|
||||
|
||||
We use the term *practically-RESTful* in order to make it clear we are not trying to be fully REST compliant but still maintain important RESTful attributes like:
|
||||
|
||||
* Stateless: platform maintains no conversational or session-based state. The result of this is ability to scale horizontally with ease.
|
||||
* Resource-oriented: API is focussed around set of resources using HTTP vocabulary and conventions e.g GET, PUT, POST, DELETE, HTTP status codes. This results in a simple and consistent API for clients.
|
||||
|
||||
See online API Documentation for more detail.
|
||||
|
||||
==== Multi-tenanted
|
||||
|
||||
The Fineract platform has been developed with support for multi-tenancy at the core of its design. This means that it is just as easy to use the platform for Software-as-a-Service (SaaS) type offerings as it is for local installations.
|
||||
|
||||
The platform uses an approach that isolates an FIs data per database/schema (See Separate Databases and Shared Database, Separate Schemas).
|
||||
|
||||
==== Extensible
|
||||
|
||||
Whilst each tenant will have a set of core tables, the platform tables can be extended in different ways for each tenant through the use of Data tables functionality.
|
||||
|
||||
==== Command Query Seperation
|
||||
|
||||
We seperate *commands* (that change data) from *queries* (that read data).
|
||||
|
||||
Why? There are numerous reasons for choosing this approach which at present is not an attempt at full blown CQRS. The main advantages at present are:
|
||||
|
||||
* State changing commands are persisted providing an audit of all state changes.
|
||||
* Used to support a general approach to *maker-checker*.
|
||||
* State changing commands use the Object-Oriented paradign (and hence ORM) whilst querys can stay in the data paradigm.
|
||||
|
||||
==== Maker-Checker
|
||||
|
||||
Also known as *four-eyes principal*. Enables apps to support a maker-checker style workflow process. Commands that pass validation will be persisted. Maker-checker can be enabled/disabled at fine-grained level for any state changing API.
|
||||
Fine grained access control
|
||||
|
||||
A fine grained permission is associated with each API. Administrators have fine grained control over what roles or users have access to.
|
||||
|
||||
=== Code Packaging
|
||||
|
||||
The intention is for platform code to be packaged in a vertical slice way (as opposed to layers).
|
||||
Source code starts from https://github.com/apache/fineract/tree/develop/fineract-provider/src/main/java/org/apache/fineract
|
||||
|
||||
* accounting
|
||||
* useradministration
|
||||
* infrastructure
|
||||
* portfolio
|
||||
** charge
|
||||
** client
|
||||
** fund
|
||||
** loanaccount
|
||||
* accounting
|
||||
|
||||
Within each vertical slice is some common packaging structure:
|
||||
|
||||
* api - XXXApiResource.java - REST api implementation files
|
||||
* handler - XXXCommandHandler.java - specific handlers invoked
|
||||
* service - contains read + write services for functional area
|
||||
* domain - OO concepts for the functional area
|
||||
* data - Data concepts for the area
|
||||
* serialization - ability to convert from/to API JSON for functional area
|
||||
|
||||
=== Design Overview
|
||||
|
||||
NOTE: The implementation of the platform code to process commands through handlers whilst supporting maker-checker and authorisation checks is a little bit convoluted at present and is an area pin-pointed for clean up to make it easier to on board new platform developers. In the mean time below content is used to explain its workings at present.
|
||||
|
||||
.CQRS
|
||||
image::command-query.png[]
|
||||
|
||||
Taking into account example shown above for the *users* resource.
|
||||
|
||||
* Query: GET /users
|
||||
* HTTPS API: retrieveAll method on *org.apache.fineract.useradministration.api.UsersApiResource* invoked
|
||||
* UsersApiResource.retrieveAll: Check user has permission to access this resources data.
|
||||
* UsersApiResource.retrieveAll: Use 'read service' to fetch all users data ('read services' execute simple SQL queries against Database using JDBC)
|
||||
* UsersApiResource.retrieveAll: Data returned to coverted into JSON response
|
||||
* Command: POST /users (Note: data passed in request body)
|
||||
* HTTPS API: create method on org.apache.fineract.useradministration.api.UsersApiResource invoked
|
||||
|
||||
.UsersApiResource.create
|
||||
[source,java]
|
||||
----
|
||||
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/useradministration/api/UsersApiResource.java[lines=168..188]
|
||||
----
|
||||
|
||||
.Create a CommandWrapper object that represents this create user command and JSON request body. Pass off responsiblity for processing to PortfolioCommandSourceWritePlatformService.logCommandSource
|
||||
[source,java]
|
||||
----
|
||||
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java[lines=69..129]
|
||||
----
|
||||
|
||||
.Check user has permission for this action. if ok, a) parse the json request body, b) create a JsonCommand object to wrap the command details, c) use CommandProcessingService to handle command
|
||||
[source,java]
|
||||
----
|
||||
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java[lines=84..150]
|
||||
----
|
||||
|
||||
NOTE: if a RollbackTransactionAsCommandIsNotApprovedByCheckerException occurs at this point. The original transaction will of been aborted and we only log an entry for the command in the audit table setting its status as 'Pending'.
|
||||
|
||||
* Check that if maker-checker configuration enabled for this action. If yes and this is not a 'checker' approving the command - rollback at the end. We rollback at the end in order to test if the command will pass 'domain validation' which requires commit to database for full check.
|
||||
* findCommandHandler - Find the correct Hanlder to process this command.
|
||||
* Process command using handler (In transactional scope).
|
||||
* CommandSource object created/updated with all details for logging to 'm_portfolio_command_source' table.
|
||||
* In update scenario, we check to see if there where really any changes/updates. If so only JSON for changes is stored in audit log.
|
||||
|
||||
=== Database support
|
||||
|
||||
Fineract supports multiple databases:
|
||||
|
||||
* MySQL compatible databases (e.g. MariaDB)
|
||||
* PostgreSQL
|
||||
|
||||
The platform differentiates between these database types in certain cases when there's a need to use some database specific tooling. To do so, the platform examines the JDBC driver used for running the platform and tries to determine which database is being used.
|
||||
|
||||
The currently supported JDBC driver and corresponding mappings can be found below.
|
||||
|
||||
[cols="1,1"]
|
||||
|===
|
||||
|*JDBC driver class name*
|
||||
|*Resolved database type*
|
||||
|
||||
|`org.mariadb.jdbc.Driver`
|
||||
|MySQL
|
||||
|
||||
|`com.mysql.jdbc.Driver`
|
||||
|MySQL
|
||||
|
||||
|`org.postgresql.Driver`
|
||||
|PostgreSQL
|
||||
|
||||
|===
|
||||
|
||||
The actual code can be found in the `DatabaseTypeResolver` class.
|
||||
|
||||
=== Data-access layer
|
||||
|
||||
The data-access layer of Fineract is implemented by using JPA (Java Persistence API) with the EclipseLink provider.
|
||||
Despite the fact that JPA is used quite extensively in the system, there are cases where the performance is a key element for an operation therefore you can easily find native SQLs as well.
|
||||
|
||||
The data-access layer of Fineract is compatible with different databases. Since a lot of the native queries are using specific database functions, a wrapper class - `DatabaseSpecificSQLGenerator` - has been introduced to handle these database specifics. Whenever there's a need to rely on new database level functions, make sure to extend this class and implement the specific functions provided by the database.
|
||||
|
||||
Fineract has been developed for 10+ years by the community and unfortunately there are places where entity relationships are configured with `EAGER` fetching strategy. This must not confuse anybody. The long-term goal is to use the `LAZY` fetching strategy for every single relationship. If you're about to introduce a new one, make sure to use `LAZY` as a fetching strategy, otherwise your PR will be rejected.
|
||||
|
||||
=== Database schema migration
|
||||
|
||||
As for every system, the database structure will and need to evolve over time. Fineract is no different. Originally for Fineract, Flyway was used until Fineract 1.6.x.
|
||||
|
||||
After 1.6.x, PostgreSQL support was added to the platform hence there was a need to make the data-access layer and the schema migration as database independent as possible. Becuase of that, from Fineract 1.7.0, Flyway is not used anymore but Liquibase is.
|
||||
|
||||
Some of the changesets in the Liquibase changelogs have database specifics into it but they only run for the relevant databases. This is controller by Liquibase contexts.
|
||||
|
||||
The currently available Liquibase contexts are:
|
||||
|
||||
* `mysql` - only set when the database is a MySQL compatible database (e.g. MariaDB)
|
||||
* `postgresql` - only set when the database is a PostgreSQL database
|
||||
* configured Spring active profiles
|
||||
* `tenant_store_db` - only set when the database migration runs the Tenant Store upgrade
|
||||
* `tenant_db` - only set when the database migration runs the Tenant upgrade
|
||||
* `initial_switch` - this is a technical context and should *NOT* be used
|
||||
|
||||
The switch from Flyway (1.6.x) to Liquibase (1.7.x) was planned to be as smooth as possible so there's no need for manual work hence the behavior is described as following:
|
||||
|
||||
* If the database is empty, Liquibase will create the database schema from scratch
|
||||
* If the database contains the latest Fineract 1.6.x database structure which was previously migrated with Flyway. Liquibase will seamlessly upgrade it to the latest version. Note: the Flyway related 2 database tables are left as they are and are not deleted.
|
||||
* If the database contains an earlier version of the database structure than Fineract 1.6.x. Liquibase will *NOT* do anything and *will fail the application during startup*. The proper approach in this case is to first upgrade your application version to the latest Fineract 1.6.x so that the latest Flyway changes are executed and then upgrade to the newer Fineract version where Liquibase will seamlessly take over the database upgrades.
|
||||
@@ -0,0 +1,5 @@
|
||||
// NOTE: no section title needed here!
|
||||
|
||||
include::properties.adoc[leveloffset=+1]
|
||||
|
||||
include::software.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,22 @@
|
||||
= Authentication Properties
|
||||
|
||||
.Authentication Properties
|
||||
|===
|
||||
|Name |Env Variable |Default Value |Description
|
||||
|
||||
|fineract.security.basicauth.enabled
|
||||
|FINERACT_SECURITY_BASICAUTH_ENABLED
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|fineract.security.oauth.enabled
|
||||
|FINERACT_SECURITY_OAUTH_ENABLED
|
||||
|false
|
||||
|TBD
|
||||
|
||||
|fineract.security.2fa.enabled
|
||||
|FINERACT_SECURITY_2FA_ENABLED
|
||||
|false
|
||||
|TBD
|
||||
|===
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
= Cache Properties
|
||||
|
||||
.Cache Properties
|
||||
|===
|
||||
|Name |Env Variable |Default Value |Description
|
||||
|
||||
|Column 1, row 1
|
||||
|Column 2, row 1
|
||||
|Column 3, row 1
|
||||
|TBD
|
||||
|
||||
|Column 1, row 2
|
||||
|Column 2, row 2
|
||||
|Column 3, row 2
|
||||
|TBD
|
||||
|===
|
||||
@@ -0,0 +1,51 @@
|
||||
= Tenant Database Properties
|
||||
|
||||
.Tenant Database Properties
|
||||
|===
|
||||
|Name |Env Variable |Default Value |Description
|
||||
|
||||
|fineract.tenant.host
|
||||
|FINERACT_DEFAULT_TENANTDB_HOSTNAME
|
||||
|localhost
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.port
|
||||
|FINERACT_DEFAULT_TENANTDB_PORT
|
||||
|3306
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.username
|
||||
|FINERACT_DEFAULT_TENANTDB_UID
|
||||
|root
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.password
|
||||
|FINERACT_DEFAULT_TENANTDB_PWD
|
||||
|mysql
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.parameters
|
||||
|FINERACT_DEFAULT_TENANTDB_CONN_PARAMS
|
||||
|
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.timezone
|
||||
|FINERACT_DEFAULT_TENANTDB_TIMEZONE
|
||||
|Asia/Kolkata
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.identifier
|
||||
|FINERACT_DEFAULT_TENANTDB_IDENTIFIER
|
||||
|default
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.name
|
||||
|FINERACT_DEFAULT_TENANTDB_NAME
|
||||
|fineract_default
|
||||
|TBD
|
||||
|
||||
|fineract.tenant.description
|
||||
|FINERACT_DEFAULT_TENANTDB_DESCRIPTION
|
||||
|Default Demo Tenant
|
||||
|TBD
|
||||
|===
|
||||
@@ -0,0 +1,116 @@
|
||||
= Hikari Connection Pool Properties
|
||||
|
||||
.Hikari Connection Pool Properties
|
||||
|===
|
||||
|Name |Env Variable |Default Value |Description
|
||||
|
||||
|spring.datasource.hikari.driverClassName
|
||||
|FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME
|
||||
|org.mariadb.jdbc.Driver
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.jdbcUrl
|
||||
|FINERACT_HIKARI_JDBC_URL
|
||||
|jdbc:mariadb://localhost:3306/fineract_tenants
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.username
|
||||
|FINERACT_HIKARI_USERNAME
|
||||
|root
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.password
|
||||
|FINERACT_HIKARI_PASSWORD
|
||||
|mysql
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.minimumIdle
|
||||
|FINERACT_HIKARI_MINIMUM_IDLE
|
||||
|3
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.maximumPoolSize
|
||||
|FINERACT_HIKARI_MAXIMUM_POOL_SIZE
|
||||
|10
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.idleTimeout
|
||||
|FINERACT_HIKARI_IDLE_TIMEOUT
|
||||
|60000
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.connectionTimeout
|
||||
|FINERACT_HIKARI_CONNECTION_TIMEOUT
|
||||
|20000
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.connectionTestquery
|
||||
|FINERACT_HIKARI_TEST_QUERY
|
||||
|SELECT 1
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.autoCommit
|
||||
|FINERACT_HIKARI_AUTO_COMMIT
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['cachePrepStmts']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_CACHE_PREP_STMTS
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['prepStmtCacheSize']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_PREP_STMT_CACHE_SIZE
|
||||
|250
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['prepStmtCacheSqlLimit']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_PREP_STMT_CACHE_SQL_LIMIT
|
||||
|2048
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['useServerPrepStmts']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_USE_SERVER_PREP_STMTS
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['useLocalSessionState']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_USE_LOCAL_SESSION_STATE
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['rewriteBatchedStatements']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_REWRITE_BATCHED_STATEMENTS
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['cacheResultSetMetadata']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_CACHE_RESULT_SET_METADATA
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['cacheServerConfiguration']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_CACHE_SERVER_CONFIGURATION
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['elideSetAutoCommits']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_ELIDE_SET_AUTO_COMMITS
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['maintainTimeStats']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_MAINTAIN_TIME_STATS
|
||||
|false
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['logSlowQueries']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_LOG_SLOW_QUERIES
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|spring.datasource.hikari.dataSourceProperties['dumpQueriesOnException']
|
||||
|FINERACT_HIKARI_DS_PROPERTIES_DUMP_QUERIES_IN_EXCEPTION
|
||||
|true
|
||||
|TBD
|
||||
|===
|
||||
@@ -0,0 +1,36 @@
|
||||
= SSL Properties
|
||||
|
||||
.SSL Properties
|
||||
|===
|
||||
|Name |Env Variable |Default Value |Description
|
||||
|
||||
|server.ssl.enabled
|
||||
|FINERACT_SERVER_SSL_ENABLED
|
||||
|true
|
||||
|TBD
|
||||
|
||||
|server.ssl.protocol
|
||||
|FINERACT_SERVER_SSL_PROTOCOL
|
||||
|TLS
|
||||
|TBD
|
||||
|
||||
|server.ssl.ciphers
|
||||
|FINERACT_SERVER_SSL_CIPHERS
|
||||
|TLS_RSA_WITH_AES_128_CBC_SHA256
|
||||
|TBD
|
||||
|
||||
|server.ssl.enabled-protocols
|
||||
|FINERACT_SERVER_SSL_PROTOCOLS
|
||||
|TLSv1.2
|
||||
|TBD
|
||||
|
||||
|server.ssl.key-store
|
||||
|FINERACT_SERVER_SSL_KEY_STORE
|
||||
|classpath:keystore.jks
|
||||
|TBD
|
||||
|
||||
|server.ssl.key-store-password
|
||||
|FINERACT_SERVER_SSL_KEY_STORE_PASSWORD
|
||||
|openmf
|
||||
|TBD
|
||||
|===
|
||||
@@ -0,0 +1,42 @@
|
||||
= Tomcat Properties
|
||||
|
||||
.Tomcat Properties
|
||||
|===
|
||||
|Name |Env Variable |Default Value |Description
|
||||
|
||||
|server.tomcat.accept-count
|
||||
|FINERACT_SERVER_TOMCAT_ACCEPT_COUNT
|
||||
|100
|
||||
|TBD
|
||||
|
||||
|server.tomcat.accesslog.enabled
|
||||
|FINERACT_SERVER_TOMCAT_ACCESSLOG_ENABLED
|
||||
|false
|
||||
|TBD
|
||||
|
||||
|server.tomcat.max-connections
|
||||
|FINERACT_SERVER_TOMCAT_MAX_CONNECTIONS
|
||||
|8192
|
||||
|TBD
|
||||
|
||||
|server.tomcat.max-http-form-post-size
|
||||
|FINERACT_SERVER_TOMCAT_MAX_HTTP_FORM_POST_SIZE
|
||||
|2MB
|
||||
|TBD
|
||||
|
||||
|server.tomcat.max-keep-alive-requests
|
||||
|FINERACT_SERVER_TOMCAT_MAX_KEEP_ALIVE_REQUESTS
|
||||
|100
|
||||
|TBD
|
||||
|
||||
|server.tomcat.threads.max
|
||||
|FINERACT_SERVER_TOMCAT_THREADS_MAX
|
||||
|200
|
||||
|TBD
|
||||
|
||||
|server.tomcat.threads.min-spare
|
||||
|FINERACT_SERVER_TOMCAT_THREADS_MIN_SPARE
|
||||
|10
|
||||
|TBD
|
||||
|===
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[appendix]
|
||||
= Fineract Application Properties
|
||||
|
||||
TBD
|
||||
|
||||
include::properties-database.adoc[leveloffset=+1]
|
||||
|
||||
include::properties-pool.adoc[leveloffset=+1]
|
||||
|
||||
include::properties-ssl.adoc[leveloffset=+1]
|
||||
|
||||
include::properties-authentication.adoc[leveloffset=+1]
|
||||
|
||||
include::properties-tomcat.adoc[leveloffset=+1]
|
||||
|
||||
// include::properties-cache.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,4 @@
|
||||
[appendix]
|
||||
= Third Party Software
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,42 @@
|
||||
= Design Overview
|
||||
|
||||
NOTE: The implementation of the platform code to process commands through handlers whilst supporting maker-checker and authorisation checks is a little bit convoluted at present and is an area pin-pointed for clean up to make it easier to on board new platform developers. In the mean time below content is used to explain its workings at present.
|
||||
|
||||
.CQRS
|
||||
image::{imagesdir}/command-query.png[]
|
||||
|
||||
Taking into account example shown above for the *users* resource.
|
||||
|
||||
* Query: GET /users
|
||||
* HTTPS API: retrieveAll method on *org.apache.fineract.useradministration.api.UsersApiResource* invoked
|
||||
* UsersApiResource.retrieveAll: Check user has permission to access this resources data.
|
||||
* UsersApiResource.retrieveAll: Use 'read service' to fetch all users data ('read services' execute simple SQL queries against Database using JDBC)
|
||||
* UsersApiResource.retrieveAll: Data returned to coverted into JSON response
|
||||
* Command: POST /users (Note: data passed in request body)
|
||||
* HTTPS API: create method on org.apache.fineract.useradministration.api.UsersApiResource invoked
|
||||
|
||||
.UsersApiResource.create
|
||||
[source,java]
|
||||
----
|
||||
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/useradministration/api/UsersApiResource.java[lines=168..188]
|
||||
----
|
||||
|
||||
.Create a CommandWrapper object that represents this create user command and JSON request body. Pass off responsiblity for processing to PortfolioCommandSourceWritePlatformService.logCommandSource
|
||||
[source,java]
|
||||
----
|
||||
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java[lines=69..129]
|
||||
----
|
||||
|
||||
.Check user has permission for this action. if ok, a) parse the json request body, b) create a JsonCommand object to wrap the command details, c) use CommandProcessingService to handle command
|
||||
[source,java]
|
||||
----
|
||||
include::{rootdir}/fineract-provider/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java[lines=84..150]
|
||||
----
|
||||
|
||||
NOTE: if a RollbackTransactionAsCommandIsNotApprovedByCheckerException occurs at this point. The original transaction will of been aborted and we only log an entry for the command in the audit table setting its status as 'Pending'.
|
||||
|
||||
* Check that if maker-checker configuration enabled for this action. If yes and this is not a 'checker' approving the command - rollback at the end. We rollback at the end in order to test if the command will pass 'domain validation' which requires commit to database for full check.
|
||||
* findCommandHandler - Find the correct Hanlder to process this command.
|
||||
* Process command using handler (In transactional scope).
|
||||
* CommandSource object created/updated with all details for logging to 'm_portfolio_command_source' table.
|
||||
* In update scenario, we check to see if there where really any changes/updates. If so only JSON for changes is stored in audit log.
|
||||
@@ -0,0 +1,41 @@
|
||||
= Functional Overview
|
||||
|
||||
As ALL capabilities of the platform are exposed through an API, The API docs are the best place to view a detailed breakdown of what the platform does. See online API Documentation.
|
||||
|
||||
.Platform Functional Overview
|
||||
image::{imagesdir}/platform-categories.png[]
|
||||
|
||||
At a higher level though we see the capabilities fall into the following categories:
|
||||
|
||||
* Infrastructure
|
||||
** Codes
|
||||
** Extensible Data Tables
|
||||
** Reporting
|
||||
|
||||
* User Administration
|
||||
** Users
|
||||
** Roles
|
||||
** Permissions
|
||||
|
||||
* Organisation Modelling
|
||||
** Offices
|
||||
** Staff
|
||||
** Currency
|
||||
|
||||
* Product Configuration
|
||||
** Charges
|
||||
** Loan Products
|
||||
** Deposit Products
|
||||
|
||||
* Client Data
|
||||
** Know Your Client (KYC)
|
||||
|
||||
* Portfolio Management
|
||||
** Loan Accounts
|
||||
** Deposit Accounts
|
||||
** Client/Groups
|
||||
|
||||
* GL Account Management
|
||||
** Chart of Accounts
|
||||
** General Ledger
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
= History
|
||||
|
||||
== The Idea
|
||||
|
||||
Fineract was an idea born out of a wish to create and deploy technology that allows the microfinance industry to scale. The goal is to:
|
||||
|
||||
* Produce a gold standard management information system suitable for microfinance operations
|
||||
* Acts as the basis of a platform for microfinance
|
||||
* Open source, owned and driven by member organisations in the community
|
||||
* Enabling potential for eco-system of providers located near to MFIs
|
||||
|
||||
== Timeline
|
||||
|
||||
* 2006: Project intiated by Grameen Foundation
|
||||
* Late 2011: Grameen Foundation handed over full responsibility to open source community.
|
||||
* 2012: Mifos X platform started. Previous members of project come together under the name of Community for Open Source Microfinance (COSM / OpenMF)
|
||||
* 2013: COSM / OpenMF officially rebranded to Mifos Initiative and receive US 501c3 status.
|
||||
* 2016: Fineract 1.x began incubation at Apache
|
||||
@@ -0,0 +1,25 @@
|
||||
= Architecture
|
||||
|
||||
This document captures the major architectural decisions in platform. The purpose of the document is to provide a guide to the overall structure of the platform; where it fits in the overall context of an MIS solution and its internals so that contributors can more effectively understand how changes that they are considering can be made, and the consequences of those changes.
|
||||
|
||||
The target audience for this report is both system integrators (who will use the document to gain an understanding of the structure of the platform and its design rationale) and platform contributors who will use the document to reason about future changes and who will update the document as the system evolves.
|
||||
|
||||
include::history.adoc[leveloffset=+1]
|
||||
|
||||
include::resources.adoc[leveloffset=+1]
|
||||
|
||||
include::system.adoc[leveloffset=+1]
|
||||
|
||||
include::functional.adoc[leveloffset=+1]
|
||||
|
||||
include::principles.adoc[leveloffset=+1]
|
||||
|
||||
include::design.adoc[leveloffset=+1]
|
||||
|
||||
include::persistence.adoc[leveloffset=+1]
|
||||
|
||||
include::validation.adoc[leveloffset=+1]
|
||||
|
||||
include::technology.adoc[leveloffset=+1]
|
||||
|
||||
include::modules.adoc[leveloffset=+1]
|
||||
+13
-13
@@ -1,8 +1,8 @@
|
||||
== Modules
|
||||
= Modules
|
||||
|
||||
Currently modules are a proof of concept feature in Fineract.
|
||||
|
||||
=== How to create a Fineract module
|
||||
== How to create a Fineract module
|
||||
|
||||
NOTE: At the moment the only module we have that follows these stricter guidelines is the example module we describe in detail in the next section. In the future we will try to split up Fineract's monolithic code base and move features (like loan product, accounts etc.) to separate modules as described here. Before that happens we need to do some code cleanups first.
|
||||
|
||||
@@ -17,7 +17,7 @@ The final folder and file structure should look somewhat like this:
|
||||
+
|
||||
[plantuml, format=svg]
|
||||
----
|
||||
include::diagrams/modules-folder-structure.puml[]
|
||||
include::{diagramsdir}/modules-folder-structure.puml[]
|
||||
----
|
||||
|
||||
1. Make sure that your new modules are present in settings.gradle:
|
||||
@@ -38,7 +38,7 @@ include ':fineract-doc'
|
||||
----
|
||||
<.> The `settings.gradle` file should contain something like this (and following).
|
||||
|
||||
=== How to replace an existing Fineract service
|
||||
== How to replace an existing Fineract service
|
||||
|
||||
Creating customizations for Fineract services is easy. The method described here will work both with our future module guidelines (aka "clean room" modules) and with the intermediary solution we will put in place to avoid major refactorings.
|
||||
|
||||
@@ -61,7 +61,7 @@ The final folder and file structure should look somewhat like this:
|
||||
+
|
||||
[plantuml, format=svg]
|
||||
----
|
||||
include::diagrams/custom-folder-structure.puml[]
|
||||
include::{diagramsdir}/custom-folder-structure.puml[]
|
||||
----
|
||||
+
|
||||
Please make sure that your `service` module's build.gradle file has a unique group:
|
||||
@@ -107,9 +107,9 @@ dependencies {
|
||||
|
||||
IMPORTANT: Do not include your custom module in `fineract-provider`'s dependency.gradle file. This creates a circular dependency and will fail your build. Instead you have to add your JAR file e. g. to the Docker image (in Fineract's libs folder; similar like we do it with Pentaho reporting). This setup will give you the best developer experience for now with proper source references until we have separate JAR files ready on Maven Central. In the next section we describe the "clean room" (read: where we want to go) type of modules and their customization/replacement; so don't be confused if you see the dependency appearing in `fineract-provider`'s dependency.gradle, in the future you'll be able to do that.
|
||||
|
||||
=== Example
|
||||
== Example
|
||||
|
||||
==== Dummy Module Structure
|
||||
=== Dummy Module Structure
|
||||
|
||||
We've created a demonstration how modules are supposed to be used. A module has usually at least two sub-modules:
|
||||
|
||||
@@ -179,7 +179,7 @@ dependencies {
|
||||
Hello: DEFAULT DUMMY!
|
||||
----
|
||||
|
||||
==== Replace the default implementation of DummyService
|
||||
=== Replace the default implementation of DummyService
|
||||
|
||||
This is why we created module `foo` in folder `module/foo`. There's only only one sub-module that contains an implementation of `DummyService` that will replace the default implementation:
|
||||
|
||||
@@ -207,7 +207,7 @@ dependencies {
|
||||
Hello: Foo!
|
||||
----
|
||||
|
||||
==== Testing
|
||||
=== Testing
|
||||
|
||||
There's a simple unit test in `fineract-provider` that demonstrates both scenarios (without any override of the default implementation and with an overriding alternative implementation)
|
||||
|
||||
@@ -237,14 +237,14 @@ The important part in the `foo` configuration is this line:
|
||||
include::{rootdir}/fineract-provider/src/test/java/org/apache/fineract/module/example/TestFooConfiguration.java[lines=26..26]
|
||||
----
|
||||
|
||||
==== Deployment
|
||||
=== Deployment
|
||||
|
||||
Modules (better: the JAR files) only need to be dropped in Fineract's `libs` folder. Dynamic loading of external JARs is provided since Fineract version 1.5.0.
|
||||
|
||||
==== How can I start replacing services now
|
||||
=== How can I start replacing services now
|
||||
|
||||
As said the "clean room" modules will take a while to arrive. In the meanwhile we'll prepare the existing monolithic code base for pluggability. As a proof of concept the services `org.apache.fineract.portfolio.note.service.NoteReadPlatformService` and `org.apache.fineract.portfolio.note.service.NoteWritePlatformService` can be replaced/overriden by custom implementations. We'll add more shortly and will list them here.
|
||||
As said the "clean room" modules will take a while to arrive. In the meanwhile we'll prepare the existing monolithic code base for pluggability. As a proof of concept the services `org.apache.fineract.portfolio.note.service.NoteReadPlatformService` and `org.apache.fineract.portfolio.note.service.NoteWritePlatformService` can be replaced/overriden by custom implementations. We'll add more shortly and will list them here. Reach out on the mailing list if you need a specific service to be replaceable.
|
||||
|
||||
==== Outlook
|
||||
=== Outlook
|
||||
|
||||
If this proof of concept is accepted we could refactor Fineract's services one by one and rearrange them in the proposed module structure. As discussed at ApacheCon 2021 there will be most likely some work to do to properly extract those modules (loan product, client, savings account etc.) from the monolithic code base. The main challenge are cross-dependencies between the _modules_, but there are major benefits if we split up the code like this. One - as demonstrated - is the replacement of existing default functionality. But with modules we'll also be able to finally publish Fineract JAR files to Maven Central. Those JAR files can be used to write extensions and customizations *WITHOUT* forking the whole Fineract Github repository. With modules users will also be able to create custom Fineract distributions, e. g. slimmed down to contain only the bare minimum needed in production.
|
||||
@@ -0,0 +1,64 @@
|
||||
= Persistence
|
||||
|
||||
TBD
|
||||
|
||||
== Database support
|
||||
|
||||
Fineract supports multiple databases:
|
||||
|
||||
* MySQL compatible databases (e.g. MariaDB)
|
||||
* PostgreSQL
|
||||
|
||||
The platform differentiates between these database types in certain cases when there's a need to use some database specific tooling. To do so, the platform examines the JDBC driver used for running the platform and tries to determine which database is being used.
|
||||
|
||||
The currently supported JDBC driver and corresponding mappings can be found below.
|
||||
|
||||
[cols="1,1"]
|
||||
|===
|
||||
|*JDBC driver class name*
|
||||
|*Resolved database type*
|
||||
|
||||
|`org.mariadb.jdbc.Driver`
|
||||
|MySQL
|
||||
|
||||
|`com.mysql.jdbc.Driver`
|
||||
|MySQL
|
||||
|
||||
|`org.postgresql.Driver`
|
||||
|PostgreSQL
|
||||
|
||||
|===
|
||||
|
||||
The actual code can be found in the `DatabaseTypeResolver` class.
|
||||
|
||||
== Data-access layer
|
||||
|
||||
The data-access layer of Fineract is implemented by using JPA (Java Persistence API) with the EclipseLink provider.
|
||||
Despite the fact that JPA is used quite extensively in the system, there are cases where the performance is a key element for an operation therefore you can easily find native SQLs as well.
|
||||
|
||||
The data-access layer of Fineract is compatible with different databases. Since a lot of the native queries are using specific database functions, a wrapper class - `DatabaseSpecificSQLGenerator` - has been introduced to handle these database specifics. Whenever there's a need to rely on new database level functions, make sure to extend this class and implement the specific functions provided by the database.
|
||||
|
||||
Fineract has been developed for 10+ years by the community and unfortunately there are places where entity relationships are configured with `EAGER` fetching strategy. This must not confuse anybody. The long-term goal is to use the `LAZY` fetching strategy for every single relationship. If you're about to introduce a new one, make sure to use `LAZY` as a fetching strategy, otherwise your PR will be rejected.
|
||||
|
||||
== Database schema migration
|
||||
|
||||
As for every system, the database structure will and need to evolve over time. Fineract is no different. Originally for Fineract, Flyway was used until Fineract 1.6.x.
|
||||
|
||||
After 1.6.x, PostgreSQL support was added to the platform hence there was a need to make the data-access layer and the schema migration as database independent as possible. Becuase of that, from Fineract 1.7.0, Flyway is not used anymore but Liquibase is.
|
||||
|
||||
Some of the changesets in the Liquibase changelogs have database specifics into it but they only run for the relevant databases. This is controller by Liquibase contexts.
|
||||
|
||||
The currently available Liquibase contexts are:
|
||||
|
||||
* `mysql` - only set when the database is a MySQL compatible database (e.g. MariaDB)
|
||||
* `postgresql` - only set when the database is a PostgreSQL database
|
||||
* configured Spring active profiles
|
||||
* `tenant_store_db` - only set when the database migration runs the Tenant Store upgrade
|
||||
* `tenant_db` - only set when the database migration runs the Tenant upgrade
|
||||
* `initial_switch` - this is a technical context and should *NOT* be used
|
||||
|
||||
The switch from Flyway (1.6.x) to Liquibase (1.7.x) was planned to be as smooth as possible so there's no need for manual work hence the behavior is described as following:
|
||||
|
||||
* If the database is empty, Liquibase will create the database schema from scratch
|
||||
* If the database contains the latest Fineract 1.6.x database structure which was previously migrated with Flyway. Liquibase will seamlessly upgrade it to the latest version. Note: the Flyway related 2 database tables are left as they are and are not deleted.
|
||||
* If the database contains an earlier version of the database structure than Fineract 1.6.x. Liquibase will *NOT* do anything and *will fail the application during startup*. The proper approach in this case is to first upgrade your application version to the latest Fineract 1.6.x so that the latest Flyway changes are executed and then upgrade to the newer Fineract version where Liquibase will seamlessly take over the database upgrades.
|
||||
@@ -0,0 +1,63 @@
|
||||
= Principles
|
||||
|
||||
== RESTful API
|
||||
|
||||
The platform exposes all its functionality via a *practically-RESTful API*, that communicates using JSON.
|
||||
|
||||
We use the term *practically-RESTful* in order to make it clear we are not trying to be fully REST compliant but still maintain important RESTful attributes like:
|
||||
|
||||
* Stateless: platform maintains no conversational or session-based state. The result of this is ability to scale horizontally with ease.
|
||||
* Resource-oriented: API is focussed around set of resources using HTTP vocabulary and conventions e.g GET, PUT, POST, DELETE, HTTP status codes. This results in a simple and consistent API for clients.
|
||||
|
||||
See online API Documentation for more detail.
|
||||
|
||||
== Multi-tenanted
|
||||
|
||||
The Fineract platform has been developed with support for multi-tenancy at the core of its design. This means that it is just as easy to use the platform for Software-as-a-Service (SaaS) type offerings as it is for local installations.
|
||||
|
||||
The platform uses an approach that isolates an FIs data per database/schema (See Separate Databases and Shared Database, Separate Schemas).
|
||||
|
||||
== Extensible
|
||||
|
||||
Whilst each tenant will have a set of core tables, the platform tables can be extended in different ways for each tenant through the use of Data tables functionality.
|
||||
|
||||
== Command Query Seperation
|
||||
|
||||
We seperate *commands* (that change data) from *queries* (that read data).
|
||||
|
||||
Why? There are numerous reasons for choosing this approach which at present is not an attempt at full blown CQRS. The main advantages at present are:
|
||||
|
||||
* State changing commands are persisted providing an audit of all state changes.
|
||||
* Used to support a general approach to *maker-checker*.
|
||||
* State changing commands use the Object-Oriented paradign (and hence ORM) whilst querys can stay in the data paradigm.
|
||||
|
||||
== Maker-Checker
|
||||
|
||||
Also known as *four-eyes principal*. Enables apps to support a maker-checker style workflow process. Commands that pass validation will be persisted. Maker-checker can be enabled/disabled at fine-grained level for any state changing API.
|
||||
Fine grained access control
|
||||
|
||||
A fine grained permission is associated with each API. Administrators have fine grained control over what roles or users have access to.
|
||||
|
||||
== Package Structure
|
||||
|
||||
The intention is for platform code to be packaged in a vertical slice way (as opposed to layers).
|
||||
Source code starts from https://github.com/apache/fineract/tree/develop/fineract-provider/src/main/java/org/apache/fineract
|
||||
|
||||
* accounting
|
||||
* useradministration
|
||||
* infrastructure
|
||||
* portfolio
|
||||
** charge
|
||||
** client
|
||||
** fund
|
||||
** loanaccount
|
||||
* accounting
|
||||
|
||||
Within each vertical slice is some common packaging structure:
|
||||
|
||||
* api - XXXApiResource.java - REST api implementation files
|
||||
* handler - XXXCommandHandler.java - specific handlers invoked
|
||||
* service - contains read + write services for functional area
|
||||
* domain - OO concepts for the functional area
|
||||
* data - Data concepts for the area
|
||||
* serialization - ability to convert from/to API JSON for functional area
|
||||
@@ -0,0 +1,5 @@
|
||||
= Resources
|
||||
|
||||
* Project URL is https://github.com/apache/fineract
|
||||
* Issue tracker is https://issues.apache.org/jira/projects/FINERACT/summary
|
||||
* Download from http://fineract.apache.org/
|
||||
@@ -0,0 +1,18 @@
|
||||
= System Overview
|
||||
|
||||
.Platform System Overview
|
||||
image::{imagesdir}/platform-systemview.png[]
|
||||
|
||||
Financial institutions deliver their services to customers through a variety of means today.
|
||||
|
||||
* Customers can call direct into branches (teller model)
|
||||
* Customers can organise into groups (or centers) and agree to meetup at a location and time with FI staff (traditional microfinance).
|
||||
* An FI might have a public facing information portal that customers can use for variety of reasons including account management (online banking).
|
||||
* An FI might be integrated into a ATM/POS/Card services network that the customer can use.
|
||||
* An FI might be integrated with a mobile money operator and support mobile money services for customer (present/future microfinance).
|
||||
* An FI might use third party agents to sell on products/services from other banks/FIs.
|
||||
|
||||
As illustrated in the above diagram, the various stakeholders leverage business apps to perform specific customer or FI related actions. The functionality contained in these business apps can be bundled up and packaged in any way. In the diagram, several of the apps may be combined into one app or any one of the blocks representing an app could be further broken up as needed.
|
||||
|
||||
The platform is the core engine of the MIS. It hides alot of the complexity that exists in the business and technical domains needed for an MIS in FIs behind a relatively simple API. It is this API that frees up app developers to innovate and produce apps that can be as general or as bespoke as FIs need them to be.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
= Technology
|
||||
|
||||
* Java: http://www.oracle.com/technetwork/java/javase/downloads/index.html
|
||||
* JAX-RS using Jersey
|
||||
* JSON using Google GSON
|
||||
* Spring I/O Platform: http://spring.io/platform
|
||||
** Spring Framework
|
||||
** Spring Boot
|
||||
** Spring Security
|
||||
** Spring Data (JPA) backed by EclipseLink
|
||||
* MySQL: http://www.oracle.com/us/products/mysql/overview/index.html
|
||||
* PostgreSQL
|
||||
|
||||
TBD
|
||||
+6
-8
@@ -1,8 +1,8 @@
|
||||
== Validation
|
||||
= Validation
|
||||
|
||||
=== Programmatic
|
||||
== Programmatic
|
||||
|
||||
Use the [DataValidatorBuilder](https://github.com/apache/fineract/search?q=%22class+DataValidatorBuilder%22), e.g. like so:
|
||||
Use the https://github.com/apache/fineract/search?q=%22class+DataValidatorBuilder%22[DataValidatorBuilder], e.g. like so:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -12,8 +12,7 @@ new DataValidatorBuilder().resource("fileUpload")
|
||||
.throwValidationErrors();
|
||||
----
|
||||
|
||||
Such code is often encapsulated in [`*Validator`](https://github.com/apache/fineract/search?q=Validator)
|
||||
classes (if more than a few lines, and/or reused from several places; avoid copy/paste), like so:
|
||||
Such code is often encapsulated in https://github.com/apache/fineract/search?q=Validator[`*Validator`] classes (if more than a few lines, and/or reused from several places; avoid copy/paste), like so:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -28,7 +27,6 @@ public class YourThingValidator {
|
||||
----
|
||||
|
||||
|
||||
=== Declarative
|
||||
== Declarative
|
||||
|
||||
[FINERACT-1229](https://issues.apache.org/jira/browse/FINERACT-1229) is an open issue about adopting
|
||||
Bean Validation for _declarative_ instead of _programmatic_ (as above) validation. Contributions welcome!
|
||||
[FINERACT-1229](https://issues.apache.org/jira/browse/FINERACT-1229) is an open issue about adopting Bean Validation for _declarative_ instead of _programmatic_ (as above) validation. Contributions welcome!
|
||||
@@ -0,0 +1,5 @@
|
||||
= Apache Software Foundation Infrastructure
|
||||
|
||||
We can order a server from Apache's infrastructure team and deploy a demo instance...
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,25 @@
|
||||
= Application Server
|
||||
|
||||
== Tomcat
|
||||
|
||||
TBD
|
||||
|
||||
== Undertow
|
||||
|
||||
TBD
|
||||
|
||||
== Jetty
|
||||
|
||||
TBD
|
||||
|
||||
== JBoss
|
||||
|
||||
TBD
|
||||
|
||||
== Weblogic
|
||||
|
||||
TBD
|
||||
|
||||
== Payara
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= AWS
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,5 @@
|
||||
= Google Cloud
|
||||
|
||||
The https://www.fineract.dev demo server runs on Google Cloud.
|
||||
|
||||
https://docs.google.com/presentation/d/1-VP4bNkc5kZ3B0yme_vYLiY1qpswnfz8ainnX5fp3l8/[The Running Fineract.dev, SRE style presentation] given at _ApacheCon 2020_ has some related background.
|
||||
+1
-28
@@ -1,24 +1,4 @@
|
||||
== Deployment
|
||||
|
||||
=== Plugins
|
||||
|
||||
Apache Fineract is extensible through plugin JARs (https://issues.apache.org/jira/browse/FINERACT-1177[FINERACT-1177]; based on
|
||||
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-executable-jar-format.html[Spring Boot's support]). To launch Fineract with plugin JARs in `libs/*.jar`, use:
|
||||
|
||||
----
|
||||
java -Dloader.path=libs/ -jar fineract-provider.jar
|
||||
----
|
||||
|
||||
The Fineract "Docker" container image's `ENTRYPOINT` uses this, see our `Dockerfile`. You could therefore build your customized Fineract distribution container image with your own `Dockerfile` using e.g. `FROM apache/fineract:latest` and then drop some plugin JARs into `/app/libs/`.
|
||||
|
||||
The WAR distribution does not directly support such plugins, but one could "explode" the WAR and drop JARs into `WEB-INF/lib`; if you know what you are doing, and feel nostalgic of the 1990s still using WARs, instead of the recommended modern Spring Boot distribution.
|
||||
|
||||
Here is a list of known 3rd-party plugin projects which can be dropped into `libs/`:
|
||||
|
||||
* https://github.com/vorburger/fineract-pentaho
|
||||
|
||||
|
||||
=== HTTPS
|
||||
= HTTPS
|
||||
|
||||
Because Apache Fineract deals with customer sensitive personally identifiable information (PII), it very strongly encourages all developers, implementors and end-users to always only use HTTPS. This is why it does not run on HTTP even for local development and enforces use of HTTPS.
|
||||
|
||||
@@ -31,10 +11,3 @@ Such products, when correctly configured, add the conventional `X-Forwarded-For`
|
||||
Alternatively, you could replace the built-in default SSL certificate with one you obtained from a Certificate Authority. We currently do not document how to do this, because we do not recommend this approach, as its cumbersome to configure and support and less secure than a managed auto rotating solution.
|
||||
|
||||
The Fineract API client supports an insecure mode (`FineractClient.Builder#insecure()`), and API users such as mobile apps may expose Settings to let end-users accept the self signed certificate. This should always be used for testing only, never in production.
|
||||
|
||||
|
||||
=== Google Cloud
|
||||
|
||||
The https://www.fineract.dev demo server runs on Google Cloud.
|
||||
|
||||
https://docs.google.com/presentation/d/1-VP4bNkc5kZ3B0yme_vYLiY1qpswnfz8ainnX5fp3l8/[The Running Fineract.dev, SRE style presentation] given at _ApacheCon 2020_ has some related background.
|
||||
@@ -0,0 +1,17 @@
|
||||
= Deployment
|
||||
|
||||
include::plugins.adoc[leveloffset=+1]
|
||||
|
||||
include::https.adoc[leveloffset=+1]
|
||||
|
||||
include::local.adoc[leveloffset=+1]
|
||||
|
||||
include::appserver.adoc[leveloffset=+1]
|
||||
|
||||
include::kubernetes.adoc[leveloffset=+1]
|
||||
|
||||
include::aws.adoc[leveloffset=+1]
|
||||
|
||||
include::google.adoc[leveloffset=+1]
|
||||
|
||||
include::apache.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,3 @@
|
||||
= Kubernetes
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Docker Compose
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,18 @@
|
||||
= Plugins
|
||||
|
||||
Apache Fineract is extensible through plugin JARs (https://issues.apache.org/jira/browse/FINERACT-1177[FINERACT-1177]; based on
|
||||
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-executable-jar-format.html[Spring Boot's support]). To launch Fineract with plugin JARs in `libs/*.jar`, use:
|
||||
|
||||
----
|
||||
java -Dloader.path=libs/ -jar fineract-provider.jar
|
||||
----
|
||||
|
||||
The Fineract "Docker" container image's `ENTRYPOINT` uses this, see our `Dockerfile`. You could therefore build your customized Fineract distribution container image with your own `Dockerfile` using e.g. `FROM apache/fineract:latest` and then drop some plugin JARs into `/app/libs/`.
|
||||
|
||||
The WAR distribution does not directly support such plugins, but one could "explode" the WAR and drop JARs into `WEB-INF/lib`; if you know what you are doing, and feel nostalgic of the 1990s still using WARs, instead of the recommended modern Spring Boot distribution.
|
||||
|
||||
Here is a list of known 3rd-party plugin projects which can be dropped into `libs/`:
|
||||
|
||||
* https://github.com/vorburger/fineract-pentaho
|
||||
|
||||
NOTE: The reporting module became our first module experiment out of necessity. We are currently developing a strategy to split up even more internals of Fineract into proper modules. Those that have an incompatible license will be hosted in a separate Git repository (probably on Github under the Mifos organisation). We'll send out an announcement as soon as we have more to say on this topic.
|
||||
@@ -0,0 +1,3 @@
|
||||
= Docker Compose
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Podman
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Rancher Docker Desktop
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,9 @@
|
||||
= Docker
|
||||
|
||||
TBD
|
||||
|
||||
include::docker-compose.adoc[leveloffset=+1]
|
||||
|
||||
include::docker-podman.adoc[leveloffset=+1]
|
||||
|
||||
include::docker-rancher.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,9 @@
|
||||
= Configuration
|
||||
|
||||
Configure your personal details properly:
|
||||
|
||||
* email
|
||||
* name
|
||||
* GPG key
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,17 @@
|
||||
= Fork
|
||||
|
||||
Make sure to disable Github Actions in your forked repository. See: https://github.com/vidakovic/fineract/settings/actions
|
||||
|
||||
TBD
|
||||
|
||||
== Upstream Configuration
|
||||
|
||||
TBD
|
||||
|
||||
== Syncing Upstream
|
||||
|
||||
TBD
|
||||
|
||||
== Pull Requests
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Git
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,7 @@
|
||||
= GPG
|
||||
|
||||
TBD
|
||||
|
||||
== Committers
|
||||
|
||||
Please make sure to provide your GPG fingerprint in your Apache committer profile at https://id.apache.org.
|
||||
@@ -0,0 +1,3 @@
|
||||
= Gradle
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Eclipse
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= IntelliJ
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= VSCode
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,9 @@
|
||||
= IDE
|
||||
|
||||
TBD
|
||||
|
||||
include::ide-intellij.adoc[leveloffset=+1]
|
||||
|
||||
include::ide-eclipse.adoc[leveloffset=+1]
|
||||
|
||||
include::ide-vscode.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,17 @@
|
||||
= Fineract Development Environment
|
||||
|
||||
TBD
|
||||
|
||||
include::git.adoc[leveloffset=+1]
|
||||
|
||||
include::gpg.adoc[leveloffset=+1]
|
||||
|
||||
include::docker.adoc[leveloffset=+1]
|
||||
|
||||
include::gradle.adoc[leveloffset=+1]
|
||||
|
||||
include::ide.adoc[leveloffset=+1]
|
||||
|
||||
include::kubernetes.adoc[leveloffset=+1]
|
||||
|
||||
include::tools.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,3 @@
|
||||
= Helm Charts
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= K3d
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Microk8s
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Minikube
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,11 @@
|
||||
= Kubernetes
|
||||
|
||||
TBD
|
||||
|
||||
include::kubernetes-minikube.adoc[leveloffset=+1]
|
||||
|
||||
include::kubernetes-microk8s.adoc[leveloffset=+1]
|
||||
|
||||
include::kubernetes-k3d.adoc[leveloffset=+1]
|
||||
|
||||
include::kubernetes-helm.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,9 @@
|
||||
= Brew
|
||||
|
||||
== MacOS
|
||||
|
||||
TBD
|
||||
|
||||
== Linux
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,10 @@
|
||||
= SDKMAN
|
||||
|
||||
We recommend using SKDMAN to manage the following developer tools:
|
||||
|
||||
- JDK
|
||||
- Spring Boot CLI
|
||||
- Gradle (if you need a global installation)
|
||||
- AsciidoctorJ
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,7 @@
|
||||
= Tools
|
||||
|
||||
TBD
|
||||
|
||||
include::tools-sdkman.adoc[leveloffset=+1]
|
||||
|
||||
include::tools-brew.adoc[leveloffset=+1]
|
||||
@@ -0,0 +1,5 @@
|
||||
= Antora
|
||||
|
||||
See: https://antora.org/
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,3 @@
|
||||
= Cheatsheet
|
||||
|
||||
TBD
|
||||
@@ -0,0 +1,5 @@
|
||||
= Tutorial
|
||||
|
||||
See: https://www.vogella.com/tutorials/AsciiDoc/article.html
|
||||
|
||||
TBD
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user