From e6c9bffad3a8d5f82c6c78ceae0579ddf45c9c9e Mon Sep 17 00:00:00 2001 From: hiondal Date: Mon, 3 Mar 2025 23:40:26 +0900 Subject: [PATCH] Add GitHub Action --- .github/workflows/cicd.yaml | 2 +- deployment/Jenkinsfile_ArgoCD | 137 ++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 56 deletions(-) diff --git a/.github/workflows/cicd.yaml b/.github/workflows/cicd.yaml index 3647e38..d489503 100644 --- a/.github/workflows/cicd.yaml +++ b/.github/workflows/cicd.yaml @@ -2,7 +2,7 @@ name: Frontend CI/CD Pipeline on: push: - branches: [main] + #branches: [main] paths: - '**' - '!**.md' diff --git a/deployment/Jenkinsfile_ArgoCD b/deployment/Jenkinsfile_ArgoCD index 8ba7725..ec71703 100644 --- a/deployment/Jenkinsfile_ArgoCD +++ b/deployment/Jenkinsfile_ArgoCD @@ -10,33 +10,70 @@ podTemplate( label: "${PIPELINE_ID}", serviceAccount: 'jenkins', containers: [ - containerTemplate(name: 'node', - image: 'node:20-slim', - ttyEnabled: true, - command: 'cat'), - containerTemplate(name: 'podman', - image: "mgoltzsche/podman", - ttyEnabled: true, - command: 'cat', - privileged: true), - containerTemplate(name: 'git', - image: 'alpine:3.19', - command: 'cat', - ttyEnabled: true) + containerTemplate(name: 'node', image: 'node:20-slim', ttyEnabled: true, command: 'cat'), + containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true), + containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true), + containerTemplate(name: 'envsubst', image: "hiondal/envsubst", command: 'sleep', args: '1h'), + containerTemplate(name: 'sonar-scanner', image: 'sonarsource/sonar-scanner-cli:latest', command: 'cat', ttyEnabled: true), + containerTemplate(name: 'git', image: 'alpine/git:latest', command: 'cat', ttyEnabled: true) ], volumes: [ - emptyDirVolume(mountPath: '/root/.azure', memory: false) + emptyDirVolume(mountPath: '/root/.azure', memory: false), + emptyDirVolume(mountPath: '/opt/sonar-scanner/.sonar/cache', memory: false) ] ) { node(PIPELINE_ID) { def props def imageTag = getImageTag() - def manifestRepo = 'cna-bootcamp/lifesub-manifest' - def manifestBranch = 'main' + def manifest = "deploy.yaml" + def namespace + def sonarScannerHome = '/opt/sonar-scanner' + def MANIFEST_REPO = "https://github.com/cna-bootcamp/lifesub-manifest.git" + def MANIFEST_BRANCH = "main" stage("Get Source") { checkout scm props = readProperties file: "deployment/deploy_env_vars" + namespace = "${props.namespace}" + } + + stage('Code Analysis & Quality Gate') { + container('node') { + sh "npm install" + sh "npm test -- --coverage --passWithNoTests" //test code 없어도 통과되게 함 + } + + container('sonar-scanner') { + withSonarQubeEnv('SonarQube') { + sh """ + ${sonarScannerHome}/bin/sonar-scanner \ + -Dsonar.projectKey=lifesub-web \ + -Dsonar.sources=src \ + -Dsonar.tests=src \ + -Dsonar.test.inclusions=src/**/*.test.js,src/**/*.test.jsx \ + -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info + """ + } + } + + timeout(time: 10, unit: 'MINUTES') { + def qg = waitForQualityGate() + if (qg.status != 'OK') { + error "Pipeline aborted due to quality gate failure: ${qg.status}" + } + } + } + + stage("Setup AKS") { + container('azure-cli') { + withCredentials([azureServicePrincipal('azure-credentials')]) { + sh """ + az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID + az aks get-credentials --resource-group ictcoe-edu --name ${props.teamid}-aks --overwrite-existing + kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f - + """ + } + } } stage('Build & Push Image') { @@ -51,14 +88,14 @@ podTemplate( sh """ podman login ${props.registry} --username \$USERNAME --password \$PASSWORD - podman build \\ - --build-arg PROJECT_FOLDER="." \\ - --build-arg REACT_APP_MEMBER_URL="${props.react_app_member_url}" \\ - --build-arg REACT_APP_MYSUB_URL="${props.react_app_mysub_url}" \\ - --build-arg REACT_APP_RECOMMEND_URL="${props.react_app_recommend_url}" \\ - --build-arg BUILD_FOLDER="deployment" \\ - --build-arg EXPORT_PORT="${props.export_port}" \\ - -f deployment/Dockerfile-lifesub-web \\ + podman build \ + --build-arg PROJECT_FOLDER="." \ + --build-arg REACT_APP_MEMBER_URL="${props.react_app_member_url}" \ + --build-arg REACT_APP_MYSUB_URL="${props.react_app_mysub_url}" \ + --build-arg REACT_APP_RECOMMEND_URL="${props.react_app_recommend_url}" \ + --build-arg BUILD_FOLDER="deployment" \ + --build-arg EXPORT_PORT="${props.export_port}" \ + -f deployment/Dockerfile-lifesub-web \ -t ${imagePath} . podman push ${imagePath} @@ -67,40 +104,30 @@ podTemplate( } } - stage('Update Manifest') { + stage('Update Manifest Repository') { container('git') { - // git과 yq 설치 - sh ''' - apk add --no-cache git curl - curl -L https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64 -o /usr/local/bin/yq - chmod +x /usr/local/bin/yq - ''' - - withCredentials([usernamePassword( - credentialsId: 'github-credentials', - usernameVariable: 'GIT_USERNAME', - passwordVariable: 'GIT_PASSWORD' - )]) { - sh ''' - git config --global user.email "jenkins@example.com" - git config --global user.name "Jenkins" - ''' - + // 임시 디렉토리 생성 + sh "mkdir -p /tmp/manifests" + + // Clone manifest repository + withCredentials([usernamePassword(credentialsId: 'github-credentials', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) { sh """ - rm -rf lifesub-manifest - git clone https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/${manifestRepo}.git + git config --global user.email "jenkins@example.com" + git config --global user.name "Jenkins Pipeline" + + git clone https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/cna-bootcamp/lifesub-manifest.git /tmp/manifests + cd /tmp/manifests + + # Update frontend image tag + if [ -f lifesub-web/deployments/lifesub-web.yaml ]; then + sed -i "s|image: ${props.registry}/${props.image_org}/lifesub-web:.*|image: ${props.registry}/${props.image_org}/lifesub-web:${imageTag}|g" lifesub-web/deployments/lifesub-web.yaml + fi + + # Commit and push changes + git add . + git commit -m "Update frontend image tag to ${imageTag}" || true + git push """ - - dir('lifesub-manifest') { - def imagePath = "${props.registry}/${props.image_org}/lifesub-web:${imageTag}" - sh """ - yq -i '.spec.template.spec.containers[0].image = "${imagePath}"' lifesub-web/deployments/lifesub-web-deployment.yaml - - git add . - git diff-index --quiet HEAD || git commit -m "Update lifesub-web image to ${imageTag}" - git push origin ${manifestBranch} - """ - } } } }