CI/CD stack

Written by Shubham Malankar

| Jul 16, 2025

4 min read

Inside the Actual CI/CD Stack Powering Production-Grade Java Releases

In this post, I’m sharing how I built a secure and automated CI/CD stack for one of my Java projects. The goal was to streamline the entire process—from code commit to deployment—while ensuring code quality, security, and traceability. I used tools like Jenkins, Maven, Checkmarx, and JFrog Artifactory, along with Python and Jinja2 for dynamic configuration. If you’re looking to build a reliable pipeline for your Java apps, this walkthrough will give you a solid foundation.

Why this matters: In modern development workflows, especially with Java ecosystems, having a CI/CD pipeline that addresses security, automation, and traceability isn’t optional—it’s essential. This practical setup showcases how our team aligns these aspects cohesively.

Why this matters:

In modern development workflows, especially with Java ecosystems, having a CI/CD pipeline that addresses security, automation, and traceability isn’t optional—it’s essential. Particularly for Java applications, which often support critical business operations, the pipeline must ensure that every change is verifiable, secure, and deployable across multiple environments without disruption.

This practical setup showcases how our team combines open-source tools and proven practices to address common challenges around integration, security scanning, artifact management, and configuration flexibility. For teams aiming to scale or operate in compliance-driven sectors, adopting such structured pipelines is not just beneficial but necessary.

Tools & Requirements

Component Role
Git Source code versioning
Maven Java build tool
Jenkins CI/CD automation
Checkmarx Static application security testing
JFrog Artifactory Artifact repository
Python & Jinja2 Dynamic config rendering
Target server (VM/Docker) Deployment destination

Project Structure

Assume your Java project has this structure:

my-java-app/
├── pom.xml
├── src/
├── Jenkinsfile
├── config-template.yml.j2
├── app_values.yml
└── generate_config.py

The pom.xml builds a .tar.gz under target/. The generate_config.py renders config.yml for deployment.

Git and Webhooks

Configure your Git repository (GitHub, GitLab, Bitbucket, etc.) to trigger Jenkins using a webhook on every push to your main or dev branch.

Setting Up Jenkins

Required Plugins:

  • Maven Integration
  • Git
  • Checkmarx plugin (or CLI support)
  • Artifactory plugin
  • Credentials plugin

Add Credentials in Jenkins:

  1. JFrog API Key(ID: jfrog-api-key)
  2. Target Server SSH Key(optional, if deploying to VM)

Jenkinsfile (Pipeline as Code)

pipeline {
agent any

environment {
ARTIFACT_NAME = “my-java-app.tar.gz”
ARTIFACTORY_URL = “https://your.jfrog.io/artifactory”
REPO_NAME = “generic-local”
}

stages {
stage(‘Checkout’) {
steps {
git ‘https://your-repo-url’
}
}

stage(‘Build’) {
steps {
sh ‘mvn clean package’
}
}

stage(‘Test’) {
steps {
sh ‘mvn test’
}
}

stage(‘Security Scan – Checkmarx’) {
steps {
sh ‘cx scan –project-name “my-java-app” –location-type folder –location-path .’
}
}

stage(‘Upload to Artifactory’) {
steps {
withCredentials([string(credentialsId: ‘jfrog-api-key’, variable: ‘JFROG_API_KEY’)]) {
sh ”’
jfrog config add artifactory-server \
–url=$ARTIFACTORY_URL \
–user=your-jfrog-username \
–apikey=$JFROG_API_KEY \
–interactive=false

jfrog rt u “target/${ARTIFACT_NAME}” “${REPO_NAME}/my-java-app/${ARTIFACT_NAME}”
”’
}
}
}

stage(‘Generate App Config’) {
steps {
sh ”’
pip install –user PyYAML Jinja2
python3 generate_config.py
”’
}
}

stage(‘Deploy to Server’) {
when {
branch ‘main’
}
steps {
withCredentials([string(credentialsId: ‘jfrog-api-key’, variable: ‘JFROG_API_KEY’)]) {
sh ”’
jfrog rt dl “${REPO_NAME}/my-java-app/${ARTIFACT_NAME}” –flat

scp ${ARTIFACT_NAME} user@target-server:/opt/myapp/
scp config.yml user@target-server:/opt/myapp/config/

ssh user@target-server ”
cd /opt/myapp &&
tar -xzf ${ARTIFACT_NAME} &&
./run.sh

”’
}
}
}
}

post {
always {
junit ‘target/surefire-reports/*.xml’
}
}
}

bulb
Why this structure:
This Jenkinsfile’s step-wise segmentation ensures clear visibility and traceability at every stage—from code checkout to deployment. It’s designed for security-conscious workflows where every stage can be audited independently.

Python Script for Config Rendering

generate_config.py:

import yaml
from jinja2 import Environment, FileSystemLoader

# Load values
with open(‘app_values.yml’) as f:
values = yaml.safe_load(f)

# Setup Jinja2
env = Environment(
loader=FileSystemLoader(‘.’),
trim_blocks=True,
lstrip_blocks=True
)
template = env.get_template(‘config-template.yml.j2’)

# Render template with values
output = template.render(values)

# Save to final config.yml
with open(‘config.yml’, ‘w’) as f:
f.write(output)

bulb
Key Insights:
Dynamic configuration rendering with Jinja2 ensures your deployments remain flexible and environment-agnostic. This avoids the pitfalls of hardcoded configurations, especially when scaling across dev, staging, and prod.

What This Pipeline Does

  1. Checks out the code
  2. Builds the Java project using Maven
  3. Runs unit tests
  4. Scans code for vulnerabilities using Checkmarx
  5. Uploads .tar.gz artifact to JFrog Artifactory
  6. Generates config.yml from template and values using Python & Jinja2
  7. Deploys the artifact and config to a target server

Best Practices

  • Use different branches for dev, QA, and prod pipelines.
  • Store sensitive data using Jenkins credentials.
  • Use artifact versioning (my-java-app-1.0.0.tar.gz) for traceability.
  • Monitor pipeline with Jenkins Blue Ocean or email notifications.
  • Add Slack/MS Teams integrations for alerts.

Conclusion

Setting up this CI/CD pipeline has been valuable for managing and deploying my Java applications. With everything integrated—Git, Maven, Jenkins, Checkmarx, Artifactory, and Python-based configuration—I now have a fully automated and secure workflow that reduces manual effort and operational risk.

I’m able to:

  • Validate code quality and security early
  • Package and version artifacts reliably
  • Generate environment-specific configs dynamically
  • Push builds to any server with a single Git push

This setup has improved traceability, security posture, and consistency in production deployments.

Next Steps: For teams looking to extend these capabilities, integrating Docker, Kubernetes, and GitOps patterns can further enhance deployment agility and scalability across cloud-native environments.

If you’re building a similar pipeline or want to adapt it for Docker, Kubernetes, or cloud-native environments, I’d be interested to hear your approach or share insights where relevant.


Go to Top