From 5f924f7ec5bfb2a4201e6965b0187e5d5f6d7030 Mon Sep 17 00:00:00 2001 From: Hongwoo Yoon <144175201+Yopdiff@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:46:07 -0700 Subject: [PATCH 01/12] Add files via upload --- Proejct_Instruction.md | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Proejct_Instruction.md diff --git a/Proejct_Instruction.md b/Proejct_Instruction.md new file mode 100644 index 00000000..f313ab56 --- /dev/null +++ b/Proejct_Instruction.md @@ -0,0 +1,58 @@ +# 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 + + +## Evaluation Criteria + +Your project will be assessed on the following criteria: + +### GitHub Repository & Git Workflow (15%) +1. Project on GitHub in a public repository with all team members participating in the development and maintenance of the project (5%). +1. Demonstrate the process practicing Git workflows (branching, pull requests, code reviews) (10%). + +### Containerization (20%) +1. Dockerfile to containerize the project (5%). +1. Use of container image repository to upload and download images (5%). +1. Effective tagging mechanism for each building matching the commits/branches/pull requests (10%). + +### CI/CD Pipeline Automation (40%) +1. Jenkins integration with GitHub in Codespace (10%). +1. Triggering automated checks upon pull request to the main branch (10%). +1. Deployment process to automatically deploy the application in the Codespace environment upon a build (10%). +1. Be able to run items 5-8 in **Existing Pipelines** (10%). + +### Testing & Code Quality (10%) +1. Generate test coverage reports upon each automated build (5%). +1. Generate code quality report using SonarQube reports upon each automated build (5%). + +### Documentation & Demo (15%) +1. Clarity and completeness of README and other documentation. The documentation must demonstrate the team’s collaboration process (5%). +1. Demonstration video with a length not exceeding 10 minutes, showing a clear understanding of the pipeline and its benefits. The documentation must demonstrate the team’s collaboration process (10%). + From 70cf34fd564cf0e3b8ccc39d8a9e97676cf1f360 Mon Sep 17 00:00:00 2001 From: Hongwoo Yoon Date: Thu, 6 Mar 2025 11:41:20 -0700 Subject: [PATCH 02/12] Initial commit --- Dockerfile | 7 +++++++ docs/BDD_video.mp4 | Bin 12191007 -> 12191004 bytes 2 files changed, 7 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..7a4f5f67 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine/java:22-jdk +WORKDIR /app + +COPY . . +RUN ./gradlew build + +CMD ["java", "-jar", "build/libs/ensf400-final-project.jar"] \ No newline at end of file diff --git a/docs/BDD_video.mp4 b/docs/BDD_video.mp4 index 27c4646cf01956f75756fca4569d3aedbc1417d6..571239ede95cfe30e98aab42f099e7dc7eba9064 100644 GIT binary patch delta 665 zcmWN=*;$oTM0_k9wjMRd$n&N#bVm6W(xQ701wha zD-ZE7Q+b4GOlJl&d6dVP#cbyAI8X2-b9st+Jk2va%X2)>d|qGyFY*!#S;S(Nu#{yi z=Ve}D1+TKwu$ooQ5WCw|Ww>3{|GeyurD|T|b=L3(Ygxy7-sCOb<{dWhF7L6C_u0f| zK41%5`H+wJm`~Wor?l}I+xeVg2Vc<6m+WL0yV=8D_VE?_`I>L|mhbqU1N^{24snGpG55U-^yS`GY?>!(W`GgTFb)Kb+?Rom`~BzlLMUY=~b5bj@Sp delta 671 zcmWO1*)|ja0Eh827)vOHqD7I&(qgGbo61fhlvF~9HcM2tGV*7bN~J7ig!UMfqG_&r z2Y2%Z-OM}mUHs1ZZaLbXww z6sB-())sBmHbrQ=b|_L&+No&mQjB7?TXBk4f)ceyd$mvdm84{)ET@{BhCqwSnTi8A zh(ji!EK`F{`4(Cyh3#hf5QbsJV?4oAjNlob;{`_X60b0ZaZF$m zuki*`uwfc+F@ssm;T_)N13uytKI03%Vjc^yV-er*9Y3&ypZJAktl&2s_~Wp Date: Thu, 6 Mar 2025 19:20:21 -0700 Subject: [PATCH 03/12] Update BDD video file --- docs/BDD_video.mp4 | Bin 12191007 -> 12191004 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/BDD_video.mp4 b/docs/BDD_video.mp4 index 27c4646cf01956f75756fca4569d3aedbc1417d6..571239ede95cfe30e98aab42f099e7dc7eba9064 100644 GIT binary patch delta 665 zcmWN=*;$oTM0_k9wjMRd$n&N#bVm6W(xQ701wha zD-ZE7Q+b4GOlJl&d6dVP#cbyAI8X2-b9st+Jk2va%X2)>d|qGyFY*!#S;S(Nu#{yi z=Ve}D1+TKwu$ooQ5WCw|Ww>3{|GeyurD|T|b=L3(Ygxy7-sCOb<{dWhF7L6C_u0f| zK41%5`H+wJm`~Wor?l}I+xeVg2Vc<6m+WL0yV=8D_VE?_`I>L|mhbqU1N^{24snGpG55U-^yS`GY?>!(W`GgTFb)Kb+?Rom`~BzlLMUY=~b5bj@Sp delta 671 zcmWO1*)|ja0Eh827)vOHqD7I&(qgGbo61fhlvF~9HcM2tGV*7bN~J7ig!UMfqG_&r z2Y2%Z-OM}mUHs1ZZaLbXww z6sB-())sBmHbrQ=b|_L&+No&mQjB7?TXBk4f)ceyd$mvdm84{)ET@{BhCqwSnTi8A zh(ji!EK`F{`4(Cyh3#hf5QbsJV?4oAjNlob;{`_X60b0ZaZF$m zuki*`uwfc+F@ssm;T_)N13uytKI03%Vjc^yV-er*9Y3&ypZJAktl&2s_~Wp Date: Thu, 6 Mar 2025 21:46:43 -0700 Subject: [PATCH 04/12] changed getty version from 3.0.4 to 3.0.5 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index ff7b120e..f0594484 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.0.5" // provides access to a database versioning tool. id "org.flywaydb.flyway" version "6.0.8" @@ -219,7 +219,7 @@ cucumberReports { // merge together all the cucumber reports with a suffix of "json" reports = files(fileTree(dir: "build/bdd", include: '*.json')) testTasksFinalizedByReport = false - projectNameOverride = "$projectname" + projectNameOverride = 'demo-app' } flyway { From 7304159ea6519fbc027478e1db9701bd96c8e7bb Mon Sep 17 00:00:00 2001 From: Lattenem Date: Thu, 6 Mar 2025 21:51:33 -0700 Subject: [PATCH 05/12] Update Dockerfile to use OpenJDK 11 and install necessary dependencies for the application now it runs a tomcat server on port 8080 with the application deployed with docker --- Dockerfile | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7a4f5f67..6490ae11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,40 @@ -FROM alpine/java:22-jdk +FROM openjdk:11 WORKDIR /app -COPY . . -RUN ./gradlew build +# get chromedriver& pipenv +RUN apt-get update && apt-get install -y \ + chromium chromium-driver \ + python3 python3-pip pipenv \ + wget unzip && \ + rm -rf /var/lib/apt/lists/* -CMD ["java", "-jar", "build/libs/ensf400-final-project.jar"] \ No newline at end of file + + +COPY gradlew . +COPY gradle ./gradle +RUN chmod +x ./gradlew + +COPY build.gradle . + +COPY src ./src + +RUN ./gradlew clean build + +# install tomcat +RUN wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.100/bin/apache-tomcat-9.0.100.tar.gz \ + && tar -xzf apache-tomcat-9.0.100.tar.gz \ + && mv apache-tomcat-9.0.100 tomcat \ + && rm apache-tomcat-9.0.100.tar.gz + + +RUN ls -l tomcat/ + +RUN mkdir -p tomcat/webapps/ + +RUN cp build/libs/app.war tomcat/webapps/ROOT.war + + + +EXPOSE 8080 + +CMD ["sh", "-c", "./tomcat/bin/catalina.sh run"] \ No newline at end of file From 35a82071e5fe1a7133734914dd077d261e76f902 Mon Sep 17 00:00:00 2001 From: Lattenem Date: Thu, 6 Mar 2025 21:58:56 -0700 Subject: [PATCH 06/12] Rename deployed WAR file from ROOT.war to demo.war in Dockerfile now the tomcat server will serve the application at port 8080/demo --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6490ae11..96a79b8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN ls -l tomcat/ RUN mkdir -p tomcat/webapps/ -RUN cp build/libs/app.war tomcat/webapps/ROOT.war +RUN cp build/libs/app.war tomcat/webapps/demo.war From 16758d704593f20abd8c3d2d822f343d619aa519 Mon Sep 17 00:00:00 2001 From: Lattenem Date: Thu, 6 Mar 2025 22:04:46 -0700 Subject: [PATCH 07/12] Update dockerfile to add comments for understanding --- Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 96a79b8b..1fe0a179 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM openjdk:11 WORKDIR /app -# get chromedriver& pipenv +# get chromedriver& pipenv & unzip then delete cache RUN apt-get update && apt-get install -y \ chromium chromium-driver \ python3 python3-pip pipenv \ @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ rm -rf /var/lib/apt/lists/* - +# gradle copy and set permission COPY gradlew . COPY gradle ./gradle RUN chmod +x ./gradlew @@ -18,6 +18,7 @@ COPY build.gradle . COPY src ./src +# build project RUN ./gradlew clean build # install tomcat @@ -27,14 +28,15 @@ RUN wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.100/bin/apache-tomcat && rm apache-tomcat-9.0.100.tar.gz -RUN ls -l tomcat/ RUN mkdir -p tomcat/webapps/ +# copy war file to tomcat webapps RUN cp build/libs/app.war tomcat/webapps/demo.war EXPOSE 8080 +# run tomcat server CMD ["sh", "-c", "./tomcat/bin/catalina.sh run"] \ No newline at end of file From 6e8f78c18d44853027faf10eca9cb48338aa13e6 Mon Sep 17 00:00:00 2001 From: Lattenem Date: Mon, 10 Mar 2025 01:31:47 -0600 Subject: [PATCH 08/12] Enhance Dockerfile and UI tests for improved headless Chrome support and environment setup --- Dockerfile | 12 +++++-- src/ui_tests/python/basic_test.py | 53 ++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1fe0a179..f87c340a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,9 +5,16 @@ WORKDIR /app RUN apt-get update && apt-get install -y \ chromium chromium-driver \ python3 python3-pip pipenv \ - wget unzip && \ + wget unzip xvfb fonts-liberation && \ rm -rf /var/lib/apt/lists/* +# Start Xvfb and set DISPLAY +ENV DISPLAY=:99 +# Set up Python environment +RUN pipenv install --python 3 \ + && pipenv install pytest requests behave behave2cucumber selenium PyHamcrest + + # gradle copy and set permission COPY gradlew . @@ -39,4 +46,5 @@ RUN cp build/libs/app.war tomcat/webapps/demo.war EXPOSE 8080 # run tomcat server -CMD ["sh", "-c", "./tomcat/bin/catalina.sh run"] \ No newline at end of file +CMD Xvfb :99 -screen 0 1920x1080x24 & \ + sh -c "./tomcat/bin/catalina.sh run" \ No newline at end of file diff --git a/src/ui_tests/python/basic_test.py b/src/ui_tests/python/basic_test.py index 859f886f..9a28ca54 100644 --- a/src/ui_tests/python/basic_test.py +++ b/src/ui_tests/python/basic_test.py @@ -9,6 +9,7 @@ from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.select import Select from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.chrome.options import Options import requests from selenium.webdriver.common.proxy import Proxy, ProxyType from hamcrest import * @@ -23,7 +24,23 @@ class TestBasic(): def setup_class(self): - self.driver = webdriver.Chrome() + chrome_options = Options() + chrome_options.add_argument("--no-sandbox") + chrome_options.add_argument("--disable-dev-shm-usage") + chrome_options.add_argument("--headless") + chrome_options.add_argument("--remote-debugging-port=9222") + chrome_options.add_argument("--disable-gpu") + chrome_options.add_argument("--disable-software-rasterizer") + chrome_options.add_argument("--disable-extensions") + chrome_options.add_argument("--disable-background-networking") + chrome_options.add_argument("--disable-background-timer-throttling") + chrome_options.add_argument("--disable-backgrounding-occluded-windows") + chrome_options.add_argument("--disable-breakpad") + chrome_options.add_argument("--disable-component-extensions-with-background-pages") + chrome_options.add_argument("--disable-features=TranslateUI,BlinkGenPropertyTrees") + chrome_options.add_argument("--disable-ipc-flooding-protection") + chrome_options.add_argument("--disable-renderer-backgrounding") + self.driver = webdriver.Chrome(options=chrome_options) self.vars = {} def teardown_class(self): @@ -86,15 +103,15 @@ def __init__(self, driver): self.driver = driver def enter_username(self, text): - login_username_field = self.driver.find_element_by_id("login_username") + login_username_field = self.driver.find_element(By.ID,"login_username") login_username_field.send_keys(text) def enter_password(self, text): - login_password_field = self.driver.find_element_by_id("login_password") + login_password_field = self.driver.find_element(By.ID,"login_password") login_password_field.send_keys(text) def enter(self): - login_button = self.driver.find_element_by_id("login_submit") + login_button = self.driver.find_element(By.ID,"login_submit") login_button.click() @@ -104,15 +121,15 @@ def __init__(self, driver): self.driver = driver def enter_username(self, text): - register_username_field = self.driver.find_element_by_id("register_username") + register_username_field = self.driver.find_element(By.ID,"register_username") register_username_field.send_keys(text) def enter_password(self, text): - register_password_field = self.driver.find_element_by_id("register_password") + register_password_field = self.driver.find_element(By.ID,"register_password") register_password_field.send_keys(text) def enter(self): - register_button = self.driver.find_element_by_id("register_submit") + register_button = self.driver.find_element(By.ID,"register_submit") register_button.click() @@ -122,11 +139,11 @@ def __init__(self, driver): self.driver = driver def register_book(self, text): - register_book_field = self.driver.find_element_by_id("register_book") + register_book_field = self.driver.find_element(By.ID,"register_book") register_book_field.send_keys(text) def enter(self): - register_button = self.driver.find_element_by_id("register_book_submit") + register_button = self.driver.find_element(By.ID,"register_book_submit") register_button.click() @@ -136,11 +153,11 @@ def __init__(self, driver): self.driver = driver def register_borrower(self, text): - register_borrower_field = self.driver.find_element_by_id("register_borrower") + register_borrower_field = self.driver.find_element(By.ID,"register_borrower") register_borrower_field.send_keys(text) def enter(self): - register_button = self.driver.find_element_by_id("register_borrower_submit") + register_button = self.driver.find_element(By.ID,"register_borrower_submit") register_button.click() @@ -150,15 +167,15 @@ def __init__(self, driver): self.driver = driver def enter_book(self, text): - book_field = self.driver.find_element_by_id("lend_book") + book_field = self.driver.find_element(By.ID,"lend_book") book_field.send_keys(text) def enter_borrower(self, text): - borrower_field = self.driver.find_element_by_id("lend_borrower") + borrower_field = self.driver.find_element(By.ID,"lend_borrower") borrower_field.send_keys(text) def enter(self): - lend_button = self.driver.find_element_by_id("lend_book_submit") + lend_button = self.driver.find_element(By.ID,"lend_book_submit") lend_button.click() @@ -168,15 +185,15 @@ def __init__(self, driver): self.driver = driver def enter_addend_a(self, text): - addend_a = self.driver.find_element_by_id("addend_a") + addend_a = self.driver.find_element(By.ID,"addend_a") addend_a.send_keys(text) def enter_addend_b(self, text): - addend_b = self.driver.find_element_by_id("addend_b") + addend_b = self.driver.find_element(By.ID,"addend_b") addend_b.send_keys(text) def enter(self): - lend_button = self.driver.find_element_by_id("math_submit") + lend_button = self.driver.find_element(By.ID,"math_submit") lend_button.click() # all the important capabilities for the Result page @@ -185,7 +202,7 @@ def __init__(self, driver): self.driver = driver def get_result_text(self): - return self.driver.find_element_by_id("result").text + return self.driver.find_element(By.ID,"result").text class LibraryPageObjectModel: From a14947bef03894cb1d0ed5104dd583d4ccdf9420 Mon Sep 17 00:00:00 2001 From: Hongwoo Yoon <144175201+Yopdiff@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:53:12 -0600 Subject: [PATCH 09/12] Update build.gradle --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index ff7b120e..8e53f9be 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,8 @@ 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" From 5ca1fb4794867f9eecd2338d67b10802fce2bf71 Mon Sep 17 00:00:00 2001 From: the-dilophosaurus-who-couldnt-spit Date: Mon, 17 Mar 2025 14:56:36 -0600 Subject: [PATCH 10/12] Updated version of gradle in build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f0594484..ec5d320c 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.5" + id "org.gretty" version "3.1.5" // provides access to a database versioning tool. id "org.flywaydb.flyway" version "6.0.8" From 8922e9b1da29d05b2eb2344beaa5ec1b4c2880d9 Mon Sep 17 00:00:00 2001 From: the-dilophosaurus-who-couldnt-spit Date: Sat, 5 Apr 2025 10:46:44 -0600 Subject: [PATCH 11/12] Add initial Jenkinsfile --- Jenkinsfile | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..35d97cb4 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,117 @@ +pipeline { + agent any + + environment { + IMAGE_NAME = 'alvinlong2311/demo-app' + COMMIT_HASH = "${env.GIT_COMMIT}" + SONARQUBE_ENV = 'alvinlong2311' // Name configured in Jenkins global config + } + + tools { + jdk 'JDK-17' // Name as configured in Jenkins + maven 'Maven-3.8.6' // Or Gradle if that's your build tool + } + + options { + skipDefaultCheckout(true) + timestamps() + } + + stages { + + stage('Checkout') { + steps { + checkout scm + } + } + + stage('Build and Test') { + steps { + sh 'mvn clean install' + } + post { + success { + junit '**/target/surefire-reports/*.xml' + } + } + } + + stage('Code Quality - SonarQube') { + steps { + withSonarQubeEnv("${SONARQUBE_ENV}") { + sh 'mvn sonar:sonar' + } + } + } + + stage('Security Analysis - OWASP DependencyCheck') { + steps { + sh ''' + mkdir -p dependency-check + dependency-check/bin/dependency-check.sh --project demo-app --scan . --format ALL --out dependency-check + ''' + } + post { + always { + archiveArtifacts artifacts: 'dependency-check/**' + } + } + } + + stage('Performance Testing - JMeter') { + steps { + sh ''' + jmeter -n -t jmeter/test-plan.jmx -l jmeter/results.jtl + ''' + } + post { + always { + archiveArtifacts artifacts: 'jmeter/results.jtl' + } + } + } + + stage('Generate Javadoc') { + steps { + sh 'mvn javadoc:javadoc' + } + post { + always { + archiveArtifacts artifacts: '**/target/site/apidocs/**/*' + } + } + } + + stage('Docker Build & Push') { + steps { + script { + def tag = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}".replaceAll('/', '-') + sh "docker build -t ${IMAGE_NAME}:${tag} ." + sh "docker tag ${IMAGE_NAME}:${tag} ${IMAGE_NAME}:latest" + withCredentials([usernamePassword(credentialsId: 'dockerhub-creds', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) { + sh ''' + echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin + docker push ${IMAGE_NAME}:${tag} + docker push ${IMAGE_NAME}:latest + ''' + } + } + } + } + + stage('Deploy to Codespace Environment') { + steps { + sh './scripts/deploy.sh' // You need to create this if not already present + } + } + + } + + post { + failure { + mail to: 'team@example.com', + subject: "Pipeline failed: ${env.JOB_NAME} [${env.BUILD_NUMBER}]", + body: "Check Jenkins for details: ${env.BUILD_URL}" + } + } +} From 1c8d783108222d0a6abb8811ef99169a25a7a24e Mon Sep 17 00:00:00 2001 From: the-dilophosaurus-who-couldnt-spit Date: Sat, 5 Apr 2025 10:50:39 -0600 Subject: [PATCH 12/12] Add Deliver stage --- Jenkinsfile | 113 ++++++++++------------------------------------------ 1 file changed, 22 insertions(+), 91 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 35d97cb4..b43181e8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,117 +1,48 @@ pipeline { - agent any - - environment { - IMAGE_NAME = 'alvinlong2311/demo-app' - COMMIT_HASH = "${env.GIT_COMMIT}" - SONARQUBE_ENV = 'alvinlong2311' // Name configured in Jenkins global config - } - - tools { - jdk 'JDK-17' // Name as configured in Jenkins - maven 'Maven-3.8.6' // Or Gradle if that's your build tool - } - + agent none options { - skipDefaultCheckout(true) - timestamps() + skipStagesAfterUnstable() } - stages { - - stage('Checkout') { - steps { - checkout scm + stage('Build') { + agent { + docker { + image 'python:2-alpine' + } } - } - - stage('Build and Test') { steps { - sh 'mvn clean install' - } - post { - success { - junit '**/target/surefire-reports/*.xml' - } + sh 'python -m py_compile sources/add2vals.py sources/calc.py' } } - - stage('Code Quality - SonarQube') { - steps { - withSonarQubeEnv("${SONARQUBE_ENV}") { - sh 'mvn sonar:sonar' + stage('Test') { + agent { + docker { + image 'qnib/pytest' } } - } - - stage('Security Analysis - OWASP DependencyCheck') { steps { - sh ''' - mkdir -p dependency-check - dependency-check/bin/dependency-check.sh --project demo-app --scan . --format ALL --out dependency-check - ''' + sh 'py.test --verbose --junit-xml test-reports/results.xml sources/test_calc.py' } post { always { - archiveArtifacts artifacts: 'dependency-check/**' + junit 'test-reports/results.xml' } } } - - stage('Performance Testing - JMeter') { - steps { - sh ''' - jmeter -n -t jmeter/test-plan.jmx -l jmeter/results.jtl - ''' - } - post { - always { - archiveArtifacts artifacts: 'jmeter/results.jtl' + stage('Deliver') { //1 + agent { + docker { + image 'cdrx/pyinstaller-linux:python2' //2 } } - } - - stage('Generate Javadoc') { steps { - sh 'mvn javadoc:javadoc' + sh '/root/.pyenv/shims/pyinstaller --onefile sources/add2vals.py' //3 } post { - always { - archiveArtifacts artifacts: '**/target/site/apidocs/**/*' - } - } - } - - stage('Docker Build & Push') { - steps { - script { - def tag = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}".replaceAll('/', '-') - sh "docker build -t ${IMAGE_NAME}:${tag} ." - sh "docker tag ${IMAGE_NAME}:${tag} ${IMAGE_NAME}:latest" - withCredentials([usernamePassword(credentialsId: 'dockerhub-creds', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) { - sh ''' - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - docker push ${IMAGE_NAME}:${tag} - docker push ${IMAGE_NAME}:latest - ''' - } + success { + archiveArtifacts 'dist/add2vals' //4 } } } - - stage('Deploy to Codespace Environment') { - steps { - sh './scripts/deploy.sh' // You need to create this if not already present - } - } - - } - - post { - failure { - mail to: 'team@example.com', - subject: "Pipeline failed: ${env.JOB_NAME} [${env.BUILD_NUMBER}]", - body: "Check Jenkins for details: ${env.BUILD_URL}" - } } -} +} \ No newline at end of file