Browse Source

Enhance build-prod script with COS upload conditions

- Made COS_TARGET configurable with a default value.
- Added checks to skip COS upload if SKIP_COS_UPLOAD is enabled or if coscli is not found, improving build flexibility and error handling.
0es 2 tháng trước cách đây
mục cha
commit
7acdcca8d2
2 tập tin đã thay đổi với 184 bổ sung1 xóa
  1. 11 1
      scripts/build-prod.sh
  2. 173 0
      scripts/build.groovy

+ 11 - 1
scripts/build-prod.sh

@@ -4,6 +4,16 @@ set -euo pipefail
 yarn build
 
 # hard coded COS path, change to your real bucket/path
-COS_TARGET="cos://gami-public-1377959011/gami-web"
+COS_TARGET="${COS_TARGET:-cos://gami-public-1377959011/gami-web}"
+
+if [[ "${SKIP_COS_UPLOAD:-0}" == "1" || "${SKIP_COS_UPLOAD:-false}" == "true" ]]; then
+  echo "SKIP_COS_UPLOAD is enabled, skipping cos upload."
+  exit 0
+fi
+
+if ! command -v coscli >/dev/null 2>&1; then
+  echo "coscli not found, skipping cos upload."
+  exit 0
+fi
 
 coscli cp .output/public "$COS_TARGET" --recursive

+ 173 - 0
scripts/build.groovy

@@ -0,0 +1,173 @@
+// ==============================
+// Pipeline config constants
+// ==============================
+def GIT_URL = 'http://8.134.139.102:10880/m1ro/gami-web'
+def GIT_BRANCH = 'main'
+
+// Deployment nodes list.
+def NODES = ['10.0.0.12']
+
+def REMOTE_USER = 'ubuntu'
+def REMOTE_DIR = '/home/ubuntu/gami-web-runtime'
+def PM2_APP = 'gami'
+
+// Required when NODES is not empty.
+def SSH_CREDENTIALS_ID = 'prod-web-server'
+
+// Jenkins credentials id of dotenv file (.env). Recommended type: "Secret file"
+def DOTENV_CREDENTIALS_ID = 'prod-web-env'
+
+// Skip coscli upload step inside yarn build:prod
+def SKIP_COS_UPLOAD = false
+
+pipeline {
+  agent {
+    docker {
+      image 'node:22'
+      args '-u root:root -v /usr/local/bin/coscli:/usr/local/bin/coscli:ro'
+    }
+  }
+
+  options {
+    timestamps()
+    skipDefaultCheckout(true)
+  }
+
+  environment {
+    CI = 'true'
+  }
+
+  stages {
+    stage('Checkout') {
+      steps {
+        checkout([
+          $class: 'GitSCM',
+          branches: [[name: "*/${GIT_BRANCH}"]],
+          userRemoteConfigs: [[url: GIT_URL]]
+        ])
+      }
+    }
+
+    stage('Install') {
+      steps {
+        sh '''
+          set -eu
+          node --version
+          if [ ! -x /usr/local/bin/coscli ]; then
+            echo "[WARN] /usr/local/bin/coscli not found or not executable on docker host mount. coscli-related steps may fail."
+          else
+            /usr/local/bin/coscli --version || true
+          fi
+          if ! command -v yarn >/dev/null 2>&1; then
+            if command -v corepack >/dev/null 2>&1; then
+              corepack enable
+            fi
+          fi
+          yarn --version
+          yarn install --frozen-lockfile
+        '''
+      }
+    }
+
+    stage('Prepare Env') {
+      when {
+        expression { return (DOTENV_CREDENTIALS_ID ?: '').trim() }
+      }
+      steps {
+        withCredentials([file(credentialsId: DOTENV_CREDENTIALS_ID, variable: 'DOTENV_FILE')]) {
+          sh '''
+            set -eu
+            cp "$DOTENV_FILE" .env
+            chmod 600 .env || true
+          '''
+        }
+      }
+    }
+
+    stage('Build') {
+      steps {
+        sh """
+          set -eu
+          SKIP_COS_UPLOAD="${SKIP_COS_UPLOAD}" yarn build:prod
+          test -d .output
+        """
+      }
+    }
+
+    stage('Archive') {
+      steps {
+        sh '''
+          set -eu
+          rm -f gami-web-runtime.tgz
+          tar -czf gami-web-runtime.tgz .output ecosystem.config.cjs
+        '''
+        archiveArtifacts artifacts: 'gami-web-runtime.tgz, ecosystem.config.cjs, .output/**', fingerprint: true
+      }
+    }
+
+    stage('Deploy') {
+      when {
+        expression { return (NODES ?: []).size() > 0 }
+      }
+      steps {
+        script {
+          def nodes = (NODES ?: [])
+            .collect { "${it}".trim() }
+            .findAll { it }
+            .unique()
+
+          if (nodes.isEmpty()) {
+            echo 'No nodes provided, skipping deploy.'
+            return
+          }
+
+          if (!(SSH_CREDENTIALS_ID ?: '').trim()) {
+            error('SSH_CREDENTIALS_ID is required for deploy stage.')
+          }
+
+          sshagent(credentials: [SSH_CREDENTIALS_ID]) {
+            nodes.each { node ->
+              def deployThisNode = input(
+                message: "Deploy to node: ${node} ?",
+                ok: 'Deploy',
+                parameters: [
+                  booleanParam(
+                    name: 'CONFIRM_DEPLOY',
+                    defaultValue: false,
+                    description: "Check to confirm deploying to ${node}"
+                  )
+                ]
+              ) as boolean
+
+              if (!deployThisNode) {
+                echo "Skipped node: ${node}"
+                return
+              }
+
+              sh """
+                set -eu
+                ssh -o StrictHostKeyChecking=no ${REMOTE_USER}@${node} 'mkdir -p ${REMOTE_DIR}'
+                scp -o StrictHostKeyChecking=no gami-web-runtime.tgz ${REMOTE_USER}@${node}:${REMOTE_DIR}/
+                if [ -f .env ]; then
+                  scp -o StrictHostKeyChecking=no .env ${REMOTE_USER}@${node}:${REMOTE_DIR}/
+                fi
+                ssh -o StrictHostKeyChecking=no ${REMOTE_USER}@${node} '
+                  set -eu
+                  cd ${REMOTE_DIR}
+                  rm -rf .output
+                  tar -xzf gami-web-runtime.tgz
+                  if [ -f .env ]; then
+                    set -a
+                    . ./.env
+                    set +a
+                  fi
+                  pm2 restart ${PM2_APP} --update-env
+                '
+              """
+            }
+          }
+        }
+      }
+    }
+  }
+}