src/utils/localdev/ contains tools for iterating on Payment Hub EE Java components without rebuilding Docker images or pushing to a registry.
The Problem: Normal testing of Java changes requires: edit → build JAR → build Docker image → push to registry → redeploy. This is slow.
The Solution: localdev.py mounts your local project directory into the running pod via a hostPath volume. You rebuild the JAR locally and restart the pod — no Docker build required.
All PHEE components are operator-managed: deployed by the PaymentHub Kubernetes operator from PaymentHubDeployment CRs in src/deployer/operators/paymenthub/config/cr/. localdev.py patches live Kubernetes Deployments directly via kubectl get/apply (k8s-direct-mode).
| File | Purpose |
|---|---|
localdev.py |
Main script — patches Deployments and manages repo checkouts |
localdev.ini |
Configuration: which components to patch and where your local repos live |
cd ~/mifos-gazelle/src/utils/localdev
# 1. Check current state
./localdev.py --status
# 2. Setup a component (checkout repo + patch deployment)
./localdev.py --setup --component importer-rdbms
# 3. Build the JAR (must use Java 17 — see note below)
cd ~/ph-ee-importer-rdbms
JAVA_HOME=/home/mifosu/jdk-17.0.2 ./gradlew build -x test
# 4. Restart the pod to pick up the new JAR
kubectl delete pod -n paymenthub -l app=ph-ee-importer-rdbms
# 5. When done, restore the original deployment
cd ~/mifos-gazelle/src/utils/localdev
./localdev.py --restore --component importer-rdbmsJava version: PHEE components target Java 17. The system Java may be newer (e.g., 23) and will fail to build. Always use:
JAVA_HOME=/home/mifosu/jdk-17.0.2 ./gradlew build -x test
For components with k8s_deploy_name in localdev.ini, the patcher:
- Runs
kubectl get deployment <name> -n <namespace> -o jsonto fetch the live Deployment - Saves the original as a JSON backup at
~/.localdev-backups/<component>-deployment.json - Modifies the Deployment spec:
- Overrides the container image with the JDK image (e.g.,
eclipse-temurin:17) - Adds
command: ["java", "-jar", "<jarpath>"] - Adds a
volumeMountat/appin the container - Adds a
hostPathvolume pointing to your local project directory
- Overrides the container image with the JDK image (e.g.,
- Applies the patched manifest via
kubectl apply -f - - Waits for rollout to complete
--restore re-applies the original JSON backup and deletes the backup file.
Operator reconciliation: The in-cluster PaymentHub operator reconciles Deployments periodically and will revert k8s-direct patches. Scale it down while developing:
kubectl scale deployment ph-ee-operator -n paymenthub --replicas=0 # ... develop ... kubectl scale deployment ph-ee-operator -n paymenthub --replicas=1Alternatively, run the operator locally (see Running the Operator Locally below).
# Show status of all components (repo branch + patch state)
./localdev.py --status
# Complete setup: checkout repo + patch deployment
./localdev.py --setup
./localdev.py --setup --component importer-rdbms # single component
# Clone repositories (components with checkout_enabled = true)
./localdev.py --checkout
./localdev.py --checkout --component channel
# Pull latest changes for existing repos
./localdev.py --update
./localdev.py --update --component channel
# Preview what would be changed without modifying anything
./localdev.py --dry-run
./localdev.py --dry-run --component importer-rdbms
# Patch deployments only (no checkout)
./localdev.py
./localdev.py --component importer-rdbms
# Restore all deployments from backups
./localdev.py --restore
./localdev.py --restore --component importer-rdbms
# Run the PaymentHub operator locally
./localdev.py --run
# Use a custom config file
./localdev.py --config /path/to/custom.inicd ~/ph-ee-importer-rdbms
# ... edit Java files ...
JAVA_HOME=/home/mifosu/jdk-17.0.2 ./gradlew build -x test
kubectl delete pod -n paymenthub -l app=ph-ee-importer-rdbms
kubectl logs -f -n paymenthub -l app=ph-ee-importer-rdbms| Fields present | Mode | Effect |
|---|---|---|
k8s_deploy_name |
k8s-direct | Patches live Kubernetes Deployment + hostPath mount |
checkout_enabled only |
checkout-only | Clones/updates repo; no deployment patching |
[paymenthub-operator] section |
operator | Run operator locally via ./gradlew run |
| Parameter | Required | Description |
|---|---|---|
[general].gazelle-home |
Yes | Root path to your mifos-gazelle clone |
k8s_deploy_name |
k8s-direct | Kubernetes Deployment name (kubectl get deployments -n paymenthub) |
k8s_namespace |
k8s-direct | Namespace of the Deployment (default: paymenthub) |
app_type |
No | springboot (default) |
image |
springboot | JDK container image, e.g. eclipse-temurin:17 |
jarpath |
springboot | Path to JAR inside container, e.g. /app/build/libs/app.jar |
hostpath |
k8s-direct | Local filesystem path mounted at /app inside the container |
checkout_enabled |
No | true to enable automatic repo clone/update |
reponame |
If checkout | Git URL (HTTPS or SSH) |
branch_or_tag |
No | Branch, tag, or commit SHA (default: main) |
checkout_to_dir |
No | Directory to clone into (default: $HOME) |
- Environment variables:
$HOME,$USER, etc. - Custom variables:
${gazelle-home}references the[general]section
[my-component]
k8s_deploy_name = ph-ee-my-component # verify with: kubectl get deployments -n paymenthub
k8s_namespace = paymenthub
app_type = springboot
image = eclipse-temurin:17
jarpath = /app/build/libs/my-component.jar
hostpath = ${HOME}/my-component-repo
checkout_enabled = true
reponame = https://github.com/openMF/my-component.git
branch_or_tag = dev
checkout_to_dir = ${HOME}| Section | Mode | Checkout branch | k8s Deployment name |
|---|---|---|---|
channel |
k8s-direct | dev | ph-ee-connector-channel |
bulk-processor |
k8s-direct | dev | ph-ee-bulk-processor |
importer-rdbms |
k8s-direct | dev | ph-ee-importer-rdbms |
connector-mojaloop |
k8s-direct | dev | ph-ee-connector-mojaloop-java |
ams-mifos |
k8s-direct | dev | ph-ee-connector-ams-mifos |
bill-pay |
k8s-direct | mifos-v2.0.0 | ph-ee-connector-bill-pay |
connector-bulk |
checkout-only | dev | — |
mock-payment |
checkout-only | dev | — |
operations-app |
checkout-only | gaz-258 | — |
operations-web |
checkout-only | dev | — |
identity-account-mapper |
checkout-only | dev | — |
zeebe-ops |
checkout-only | dev | — |
connector-mccbs |
checkout-only | dev | — (separate operator) |
connector-gsma |
checkout-only | dev | — |
integration-test |
checkout-only | tomdev1 | — (test suite) |
notifications |
checkout-only | dev | — |
Instead of patching individual Deployments, you can run the PaymentHub operator locally against the cluster. The operator reads PaymentHubDeployment CRs and reconciles Deployments — running it locally lets you modify operator logic without building a Docker image.
# Scale down the in-cluster operator first (avoids dual reconcilers)
kubectl scale deployment ph-ee-operator -n paymenthub --replicas=0
# Run the local operator
cd ~/mifos-gazelle/src/utils/localdev
./localdev.py --run
# (or directly: cd ~/mifos-operators/paymenthub-operator && ./gradlew run)
# When done, restore the in-cluster operator
kubectl scale deployment ph-ee-operator -n paymenthub --replicas=1./localdev.py --run checks out the operator repo if not already present (configured under [paymenthub-operator] in localdev.ini).
# Verify the JAR was built
ls -lh ~/your-project/build/libs/
# Check hostPath is mounted in the running pod
kubectl get pod -n paymenthub -l app=your-component -o yaml | grep -A5 hostPath
# Check startup logs
kubectl logs -n paymenthub -l app=your-component# Use Java 17 explicitly — system Java may be newer
JAVA_HOME=/home/mifosu/jdk-17.0.2 ./gradlew build -x testkubectl describe pod -n paymenthub -l app=your-component
kubectl logs -n paymenthub -l app=your-component
# Verify the JAR exists inside the container
kubectl exec -n paymenthub deployment/your-component -- ls -la /app/build/libs/Common causes:
- Wrong JAR filename in
jarpath— checkbuild.gradlefor the actual artifact name - JAR not yet built — run
JAVA_HOME=/home/mifosu/jdk-17.0.2 ./gradlew build -x test hostpathdirectory does not exist on the node
The in-cluster PaymentHub operator reconciles Deployments and will overwrite patches. Scale it down:
kubectl scale deployment ph-ee-operator -n paymenthub --replicas=0# Check backup exists
ls ~/.localdev-backups/
# Manual restore
kubectl apply -f ~/.localdev-backups/<component>-deployment.json
rm ~/.localdev-backups/<component>-deployment.jsonWhen using hostPath mounts, the Spring Boot JAR contains the compiled YAML configuration. Editing application.yaml alone is not sufficient — rebuild the JAR:
JAVA_HOME=/home/mifosu/jdk-17.0.2 ./gradlew build -x test
kubectl delete pod -n paymenthub -l app=your-componentConfigMap changes do NOT take effect when using hostPath mounts — the in-JAR config takes precedence.
chmod 755 ~/your-project
chmod 644 ~/your-project/build/libs/*.jar- Single-node only:
hostPathis node-specific. This tooling is designed for single-node k3s local development. - After operator reconcile: k8s-direct patches may be overwritten. Scale down the operator or re-run
./localdev.py --component <name>(run--restorefirst if backup already exists). - Remote debugging: Add JVM debug flags to the command in the patched deployment, then
kubectl port-forward deployment/your-component 5005:5005 -n paymenthub.