diff --git a/.gitignore b/.gitignore index 09eb20a6..203c70d8 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ node_modules/ .classpath .project .settings/ +jenkins/jenkins_home/ diff --git a/README(2).md b/README(2).md new file mode 100644 index 00000000..49dc444e --- /dev/null +++ b/README(2).md @@ -0,0 +1,74 @@ +# ENSF 400 - Winter 2025 - Course Project + +## Project Overview + +In this project, you will work based on a software project by incorporating/extending a complete CI/CD (Continuous Integration/Continuous Deployment) pipeline. This is based on an open-source sample application: https://github.com/7ep/demo + +This project can also be any application that requires the project of build, test, and deployment. +You will leverage GitHub for source control, Docker for containerizing your application, and a CI/CD tool (Jenkins) to automate the build, testing, and verification process. The goal is to validate every code change automatically through container builds, unit tests, code quality checks, and end-to-end functional tests. + + +## Project Requirements + +By the end of this project, your group must deliver the following: + +1. Manage your project on GitHub and follow proper Git workflows (branching, pull requests, code reviews). Document the process of how you use Git workflows to collaborate with your team members. + +1. Containerize your application for builds and deployments. Upload and download your container images to a public or private image repository (e.g., Docker Hub or GitHub Container Registry). Ensure a container image is built with unique build tag(s) matching the triggering commit from any branch. + +1. Set up an automated CI/CD with Jenkins in a Codespace environment. Configure the pipeline to trigger upon pull requests merging changes into the main branch. + +1. Document the CI/CD process and provide clear instructions on replicating your environment. Submit a video demo at the end of the project. + +### Existing Pipelines + +You will also demonstrate the delivery of the following process and artifacts that come with the project. + +1. Run static analysis quality-gating using SonarQube +1. Performance testing with Jmeter +1. Security analysis with OWASP's "DependencyCheck" +1. Build Javadocs +___________________________________________________________________________________________________________________________________________________ + +# Organization of our ENSF 400 CI/CD Project + +## Team Members + +- Josral Frederick UCID: 30195360 +- Muhammad Aun Raza My UCID: 30172183 +- Natnael Tekli UCID: 30171167 +- Jaden Chow UCID: 30173676 + +## Project Description +The main objective in this project is to create software that incorporates/extends a complete CI/CD +(Continuous Integration/Continuous Deployment) pipeline. + +## Git Workflow +Our team follows a structured Git workflow to manage our project efficiently on GitHub. + +We begin by cloning the repository using git clone . Before making changes, we create a new feature branch with git checkout -b feature/feature-name to keep our work organized. + +Once changes are made, we stage and commit them using git add . and git commit -m "Description of changes", ensuring clear commit messages. + +To keep our branch up-to-date, we sync it with the main branch by switching to main, pulling the latest changes (git pull origin main), and merging them into our feature branch (git merge main). If merge conflicts arise, we resolve them before proceeding. + +After finalizing our changes, we push our branch to the remote repository using git push origin feature/feature-name. We then open a Pull Request (PR) on GitHub, providing a description of our modifications. + +Team members participate in a code review process, leaving feedback and suggesting necessary improvements. Once approved, the PR is merged into main, typically by a team lead or someone with write access. + +After merging, we delete the feature branch both locally (git branch -d feature/feature-name) and remotely (git push origin --delete feature/feature-name). To stay updated, we sync our local repository with the latest changes using git pull origin main. + +We follow GitFlow best practices, using descriptive branch names (e.g., feature/login-page, bugfix/error-handling) and ensuring every change is reviewed via a PR before merging into main. +External contributors fork the repository, create a new branch, make their changes, and open a PR to contribute to the project. + +This workflow ensures a structured and collaborative development process, keeping our codebase clean and organized. + +## Containerization + +To containerize our application, we use Docker and Docker Hub. After creating and reviewing the Dockerfile as a team, we build the Docker image with a unique tag matching the commit hash or branch name. This ensures traceability by linking each image to the exact code version it was built from. We generate the commit hash using COMMIT_HASH=$(git rev-parse --short HEAD), build the image with docker build -t natnaelt2/ensf400-demo:$COMMIT_HASH ., and push it to Docker Hub using docker push natnaelt2/ensf400-demo:$COMMIT_HASH. This process guarantees consistency and simplifies tracking across deployments. + +To deploy the application, we pull the Docker image from Docker Hub using the command docker pull natnaelt2/ensf400-demo:commit-hash. Finally, we run the Docker container using the command docker run -p 8080:8080 natnaelt2/ensf400-demo:commit-hash. Then visit the application with your browser at http://localhost:8080/demo. + + + + diff --git a/build.gradle b/build.gradle index ff7b120e..06d04e4c 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { // gretty is a gradle plugin to make it easy to run a server and hotswap code at runtime. // https://plugins.gradle.org/plugin/org.gretty - id 'org.gretty' version '3.0.4' + id 'org.gretty' version '3.1.5' // provides access to a database versioning tool. id "org.flywaydb.flyway" version "6.0.8" diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..b09d4254 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,50 @@ +services: + jenkins: + build: ./jenkins + privileged: true + user: root + ports: + - 8081:8080 + - 50000:50000 + container_name: jenkins + volumes: + - /home/codespace:/var/jenkins_home + - /var/run/docker.sock:/var/run/docker.sock + networks: + - dev-network + depends_on: + - sonarqube + + sonarqube: + image: sonarqube:8.9-community + container_name: sonarqube + environment: + - SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar + - SONARQUBE_JDBC_USERNAME=sonar + - SONARQUBE_JDBC_PASSWORD=sonar + ports: + - "9000:9000" + volumes: + - sonarqube_data:/opt/sonarqube/data + networks: + - dev-network + + db: + image: postgres:latest + container_name: sonar-db + environment: + - POSTGRES_USER=sonar + - POSTGRES_PASSWORD=sonar + - POSTGRES_DB=sonar + volumes: + - db_data:/var/lib/postgresql/data + networks: + - dev-network + +networks: + dev-network: + +volumes: + jenkins_home: + sonarqube_data: + db_data: diff --git a/dockerfile b/dockerfile new file mode 100644 index 00000000..979f4136 --- /dev/null +++ b/dockerfile @@ -0,0 +1,6 @@ +FROM gradle:7.6.1-jdk11 +WORKDIR /ensf400-demo +COPY . . +RUN ./gradlew build +EXPOSE 8080 +CMD ["gradle", "appRun"] diff --git a/docs/BDD_video.mp4 b/docs/BDD_video.mp4 index 27c4646c..571239ed 100644 Binary files a/docs/BDD_video.mp4 and b/docs/BDD_video.mp4 differ diff --git a/jenkins/Dockerfile b/jenkins/Dockerfile new file mode 100644 index 00000000..92ab4204 --- /dev/null +++ b/jenkins/Dockerfile @@ -0,0 +1,21 @@ +FROM jenkins/jenkins:alpine +USER root + +RUN apk add --update docker openrc + +RUN apk add --no-cache \ + openjdk11 \ + bash \ + docker \ + curl \ + unzip +ENV GRADLE_VERSION=7.6 +ENV GRADLE_HOME=/opt/gradle + +RUN mkdir -p ${GRADLE_HOME} && \ + curl -fsSL https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip -o /tmp/gradle.zip && \ + unzip /tmp/gradle.zip -d /opt/gradle && \ + rm /tmp/gradle.zip + +ENV PATH="${GRADLE_HOME}/gradle-${GRADLE_VERSION}/bin:${PATH}" +RUN gradle -v \ No newline at end of file diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile index d37024fe..8acb9365 100644 --- a/jenkins/Jenkinsfile +++ b/jenkins/Jenkinsfile @@ -8,6 +8,9 @@ pipeline { // This is set so that the Python API tests will recognize it // and go through the Zap proxy waiting at 9888 HTTP_PROXY = 'http://127.0.0.1:9888' + // Default Java Home for Jenkins (JDK 17) + JAVA_HOME = '/usr/lib/jvm/java-17-openjdk' + PATH = "${JAVA_HOME}/bin:${PATH}" } stages { @@ -15,63 +18,62 @@ pipeline { // build the war file (the binary). This is the only // place that happens. stage('Build') { + environment { + // Override JAVA_HOME to use JDK 11 for this stage + JAVA_HOME = '/usr/lib/jvm/java-11-openjdk' + PATH = "${JAVA_HOME}/bin:${PATH}" + } steps { sh './gradlew clean assemble' } - } - + @@ -23,6 +31,11 @@ pipeline { // run all the unit tests - these do not require anything else // to be running and most run very quickly. stage('Unit Tests') { + environment { + // Override JAVA_HOME to use JDK 11 for this stage + JAVA_HOME = '/usr/lib/jvm/java-11-openjdk' + PATH = "${JAVA_HOME}/bin:${PATH}" + } steps { sh './gradlew test' } - post { - always { - junit 'build/test-results/test/*.xml' - } - } - } - + @@ -36,6 +49,11 @@ pipeline { // run the tests which require connection to a // running database. stage('Database Tests') { + environment { + // Override JAVA_HOME to use JDK 11 for this stage + JAVA_HOME = '/usr/lib/jvm/java-11-openjdk' + PATH = "${JAVA_HOME}/bin:${PATH}" + } steps { sh './gradlew integrate' } - post { - always { - junit 'build/test-results/integrate/*.xml' - } - } - } - - // These are the Behavior Driven Development (BDD) tests + @@ -50,6 +68,11 @@ pipeline { // See the files in src/bdd_test // These tests do not require a running system. stage('BDD Tests') { + environment { + // Override JAVA_HOME to use JDK 11 for this stage + JAVA_HOME = '/usr/lib/jvm/java-11-openjdk' + PATH = "${JAVA_HOME}/bin:${PATH}" + } steps { sh './gradlew generateCucumberReports' // generate the code coverage report for jacoco - sh './gradlew jacocoTestReport' - } - post { - always { - junit 'build/test-results/bdd/*.xml' - } - } - } - + @@ -65,147 +88,15 @@ pipeline { // Runs an analysis of the code, looking for any // patterns that suggest potential bugs. stage('Static Analysis') { - steps { - sh './gradlew sonarqube' - // wait for sonarqube to finish its analysis - sleep 5 - sh './gradlew checkQualityGate' - } - } + environment { + // Override JAVA_HOME to use JDK 11 for this stage + JAVA_HOME = '/usr/lib/jvm/java-11-openjdk' + PATH = "${JAVA_HOME}/bin:${PATH}" + } + + steps{ + sh './gradlew sonarqube -Dsonar.host.url=http://sonarqube:9000 -Dsonar.login="admin" -Dsonar.password="ensf400"' // Move the binary over to the test environment and